#!/usr/bin/env ruby

require 'pp'
require 'open3'
require 'logger'
require 'optparse'
require 'fileutils'
require_relative 'resultnode.rb'

# Globals
$options = {
    :no_remove_build => false,      # No remove build output after compile
    :ignore_frr_patches => false,   # Ignore FRR patches
    :ignore_configure => false,     # Ignore autoreconf and build environment configure

    # Components build options
    :is_build_frr => true,
    :is_build_c_ares => true,
    :is_rebuild => false
}

# Command syntaxs
global = OptionParser.new do |opts|
    opts.banner = """Usage: build-quagga.rb [-h] [-n] [-i] [-r {c_ares|frr}]"""
    opts.separator ""
    opts.separator "<args>:"

    opts.on("-h", "--help", "Help message") do
        puts opts
        exit
    end

    opts.on("-n", "--no-remove-build", "No remove build output after compile") do
        $options[:no_remove_build] = true
    end

    opts.on("-i", "--ignore-frr-patches", "Ignore patches applied") do
        $options[:ignore_frr_patches] = true
    end

    opts.on("-r", "--rebuild", "Rebuild (based on current source)") do
        $options[:no_remove_build] = true
        $options[:ignore_configure] = true
        $options[:is_rebuild] = true
    end

    opts.separator ""
    opts.separator "Argument '-r' optional commands:
    . Make sure 'ruby build-quagga.rb -n' has been executed before
    c_ares  Rebuild c_ares (A C library for asynchronous name resolves)
    frr     Rebuild FRR (Based on current source, '-n' and '-i' are included)
    "

    opts.separator "Examples:
    ruby build-quagga.rb            Clean build
    ruby build-quagga.rb -n         Clean build and the build output aren't removed
    ruby build-quagga.rb -r frr     Rebuild FRR OSPF (based on current source)
    "
end # OptionParser

global.order!

# Check syntax: -r {ospf|rip}
if $options[:is_rebuild] == true
    $cmd = ARGV.shift

    case $cmd
    when "c_ares"
        $options[:is_build_c_ares] = true
        $options[:is_build_frr] = false
    when "frr"
        $options[:is_build_c_ares] = false
        $options[:is_build_frr] = true
        $options[:ignore_frr_patches] = true
        $options[:ignore_configure] = true
    else # default case
        puts "Invalid command: #{$cmd}"
        puts global
        exit
    end # case
end

$arch_conf = {
   'mips' => { :tc => "2018.05-013", :prefix => "mipsel", :br => "2018.05" },
    'x86'  => {  }
}

$src_conf = {
    "c-ares" => { :version => "1.12.0",
                  :file => "c-ares-1.12.0.tar.gz",
                  :url  => "https://c-ares.haxx.se/download/c-ares-1.12.0.tar.gz",
                  :md5  => "2ca44be1715cd2c5666a165d35788424"
                },

		"frr"    => { :version => "5.0",
		:file => "frr-5.0.tar.gz",
                 # :url  => "https://github.com/FRRouting/frr/archive/frr-4.0.tar.gz",
		 # :url => "https://github.com/FRRouting/frr/releases/tag/frr-5.0.1",
		  :url => "https://github.com/FRRouting/frr/archive/frr-5.0.1.tar.gz",
                  #:md5  => "689397bff284a6b50d6fac8a5f78d141",
                  :md5  => "300fa194068a912c11f02efa6eee4cc8",
                  :patches => []
		  #"frr-5.0-enable-vty-socket-without-libreadline.patch",
                   #            "frr-5.0-fix-retransmit-intervals.patch",
                    #           "frr-5.0-fix-neighbor-status.patch",
                     #          "frr-5.0-fix-neighbor-detail.patch",
                      #         "frr-5.0-fix-ospf-route-status.patch",
                       #        "frr-5.0-as-external-cost.patch"]
                              # "frr-4.0-database-detailed-json.patch",
                               #"frr-5.0-database-json.patch",
                               #"frr-5.0-fix-ospf-route-status-multiple-areas.patch"
                             #  "frr-4.0-fix-ospf-route-status-multiple-border-rt-type.patch",
                             #  "frr-4.0-fix-ospf-virtuallink-auth-type.patch",
                             #  "frr-4.0-fix-ospf-route-status-performance.patch"
                }
}

$base = "#{File.expand_path(File.dirname(File.dirname(__FILE__)))}"
$dl = "#{$base}/src"

$l = Logger.new(STDOUT)
$l.level = Logger::INFO
log_fmt = proc do |severity, datetime, progname, msg|
    "#{severity} [#{Time.now.strftime('%H:%M:%S')}] #{Thread.current.object_id.to_s}: #{msg}\n"
end
$l.formatter = log_fmt

def run(cmd)
  $l.info "Running '#{cmd}' ..."
  o=%x(#{cmd})
  if $? != 0
    $l.fatal "Running '#{cmd}' failed"
    puts o if(o)
    raise "Running '#{cmd}' failed"
  end
  return o.chomp
end

def sys(cmd)
  $l.info "Running '#{cmd}' ..."
  system "sh -c \"#{cmd}\""
  if $? != 0
    $l.fatal "Running '#{cmd}' failed"
    raise "Running '#{cmd}' failed"
  end
end

def md5chk f, md5
    return false if not File.exist?(f)
    return run("md5sum #{f}").split[0] == md5
end

$arch_conf.each do |k, v|
    #bsppath = "bsp/#{v[:tc]}"
    #bsppath += "@#{v[:br]}" if v[:br]
    bsppath = "/home/admin/bsp/mscc-brsdk-source-2018.05-013"
   #run "sudo /usr/local/bin/mscc-install-pkg -t #{bsppath} mscc-brsdk-#{k}-#{v[:tc]};" if v[:tc]
   # run "sudo /usr/local/bin/mscc-install-pkg -t /home/admin/bsp/mscc-brsdk-source-2018.05-013"
$tool_dir   = "2019.02.4-080"
$tool_file  = "2018.05-013"

sdk_install = "/usr/local/bin/mscc-install-pkg"
if File.exists?(sdk_install)
    sys "toolchain", "sudo #{sdk_install} -t toolchains/#{$tool_dir} mscc-toolchain-bin-#{$tool_file}"
elsif not File.exists?("/opt/mscc/mscc-toolchain-bin-#{$tool_file}")
    puts "ABORT: Required toolchain: mscc-toolchain-bin-#{$tool_file} doesn't exits"
    exit
else
    puts "FOUND : Required toolchain:"
end


end

run "mkdir -p #{$dl}"
$src_conf.each do |k, v|
    if not md5chk "#{$dl}/#{v[:file]}", v[:md5]
        sys "wget -O #{$dl}/#{v[:file]} #{v[:url]}"
        if not md5chk "#{$dl}/#{v[:file]}", v[:md5]
            raise "MD5 sum of #{k} did not match!"
        end
    end
end

def def_env arch, install_root
    return {} if arch == "x86"
    tc_base = "/opt/mscc/mscc-brsdk-#{arch}-#{$arch_conf[arch][:tc]}/stage2/smb/x86_64-linux"
    tc_sr = "#{tc_base}/usr/#{$arch_conf[arch][:prefix]}-buildroot-linux-gnu/sysroot"
    install_root="/home/hss/pankaj/RIP_SUPPORT_new/src/vtss_appl/frr/daemon/frr-4.0-9df25f6@master/build/zebra_only/mips/root"
    return {
        #"LD_LIBRARY_PATH" =>  "/usr/lib:/lib:/usr/local/lib:#{tc_base}/usr/lib",
        "LD_LIBRARY_PATH" =>  "#{tc_base}/usr/lib",
        #"PATH" =>             "/usr/bin:/bin:/usr/local/bin:/usr/local/sbin:#{tc_base}/usr/bin",
        "PATH" =>             "#{tc_base}/usr/bin:/usr/bin:/bin:/usr/local/bin:/usr/local/sbin",
        "LD" =>               "#{$arch_conf[arch][:prefix]}-linux-ld",
        "CC" =>               "#{$arch_conf[arch][:prefix]}-linux-gcc",
        "GCC" =>              "#{$arch_conf[arch][:prefix]}-linux-gcc",
        "STRIP" =>            "#{$arch_conf[arch][:prefix]}-linux-strip",
        "CFLAGS" =>           "-Os",
        "CPPFLAGS" =>         "-I#{install_root}/usr/include",
        "LDFLAGS" =>          "-L#{install_root}/usr/lib",
        "PKG_CONFIG_PATH" =>  "#{install_root}/usr/lib/pkgconfig",
    }
end

def env2str env
    env.to_a.map{|x| "#{x[0]}=#{x[1]}"}.join(" ")
end

def build_c_ares o, arch
    pre = $arch_conf[arch][:prefix]
    root = "#{o}/#{arch}/root"
    if $options[:no_remove_build] == false
        run "rm -rf  #{o}/#{arch}/c-ares"
    end
    run "mkdir -p #{o}/#{arch}/c-ares"
    run "mkdir -p #{o}/#{arch}/license/c-ares"
    Dir.chdir("#{o}/#{arch}/c-ares") do
        env = def_env arch, "#{o}/#{arch}/root"
        run "tar --strip-components=1 -xf #{$dl}/#{$src_conf["c-ares"][:file]}"
        run "cp LICENSE.md #{o}/#{arch}/license/c-ares"
        envs = env2str(env)

        if $options[:ignore_configure] == false
            c = " ./configure"
            c += " --target=#{pre}-linux"
            c += " --host=#{pre}-linux"
            c += " --build=x86_64-unknown-linux-gnu"
            c += " --prefix=#{root}/usr"
            c += " --sysconfdir=#{root}/etc"
            sys "pwd"
            sys "#{envs} #{c}"
        end

        sys "#{envs} make -j12"
        sys "#{envs} make install"
    end
end

def build_frr_configure pre, envs, flags
    c = "../frr/configure"
    c += " --host=#{pre}-linux"
    c += " --build=x86_64-unknown-linux-gnu"
    c += " --prefix=/usr"
    c += " --sysconfdir=/etc"
    c += " --localstatedir=/tmp"
    c += " --sharedstatedir=/tmp"
    c += " --enable-user=root"
    c += " --enable-group=root"
    c += " --program-prefix=\"\""
    c += " --disable-libtool-lock"
    c += " --disable-vtysh"
    c += " --disable-doc"

    #if flags.include? :zebra
        c += " --enable-zebra"
    #else
    #    c += " --disable-zebra"
   # end

    #c += " --enable-bgpd"
    c += " --disable-bgpd"

    #if flags.include? :rip
        c += " --enable-ripd"
    #else
     #   c += " --disable-ripd"
    #end
    c += " --enable-pbrd"
    c += " --disable-ripngd"

    #if flags.include? :ospf
        c += " --enable-ospfd"
    #else
     #   c += " --disable-ospfd"
    #end

    c += " --enable-pimd"
    c += " --enable-pimreg"
    
    #c += " --enable-ospf6d"
    c += " --disable-ospf6d"
    c += " --disable-nhrpd"
    c += " --disable-isisd"
    #c += " --disable-pimd"
    c += " --disable-ldpd"
    c += " --disable-bgp-announce"
    c += " --disable-ospfapi"
    c += " --disable-ospfclient"
    c += " --disable-watchfrr"

    sys "#{envs} #{c}"
end # build_frr_configure

def build_frr_hosttools o, arch
    run "mkdir -p #{o}/#{arch}/frr_build/hosttools"
    Dir.chdir("#{o}/#{arch}/frr_build/hosttools") do
        c = "../../frr/configure"
        c += " --host=x86_64-unknown-linux-gnu"
        c += " --build=x86_64-unknown-linux-gnu"
        c += " --enable-clippy-only"
        c += " --disable-nhrpd"
        c += " --disable-vtysh"
        sys "#{c}"
        sys "make lib/clippy"
    end
end # build_frr_hosttools

def build_frr o, arch, flags
#	sys "cd /home/vaibhav/IStaX-4.6.0/src/vtss_appl/frr/daemon/frr-4.0-9df25f6\@master/build/zebra_only/mips/root/usr/lib/"
#	sys "cp -f /opt/mscc/mscc-brsdk-mips-2018.05-013/stage2/smb/x86_64-linux/usr/lib/libzmq.la ."
#	sys "cp -rf /opt/mscc/mscc-brsdk-mips-2018.05-013/stage2/smb/x86_64-linux/usr/lib/.libs ."
#	sys "cd -"
    pre = $arch_conf[arch][:prefix]
    root = "#{o}/#{arch}/root"
    if $options[:no_remove_build] == false
        run "rm -rf  #{o}/#{arch}/frr"
    end
    run "mkdir -p #{o}/#{arch}/frr"
    run "mkdir -p #{o}/#{arch}/license/frr"
    Dir.chdir("#{o}/#{arch}/frr") do
        env = def_env arch, "#{o}/#{arch}/root"
        if $options[:ignore_frr_patches] == false
            run "tar --strip-components=1 -xf #{$dl}/#{$src_conf["frr"][:file]}"
            $src_conf["frr"][:patches].each do |p|
                run "patch -p1 < #{$base}/#{p}"
            end
        end
        run "cp COPYING #{o}/#{arch}/license/frr"
        envs = env2str(env)

        if $options[:ignore_configure] == false
            sys "autoreconf -i"
        end

        run "mkdir -p #{o}/#{arch}/frr_build"
        Dir.chdir("#{o}/#{arch}/frr_build") do
            if $options[:ignore_configure] == false
                build_frr_configure pre, envs, flags
                build_frr_hosttools o, arch
            end # if $options[:ignore_configure] == false

            sys "#{envs} make V=1 "
            sys "#{envs} make DESTDIR=#{root} install"
        end # Dir.chdir("#{o}/#{arch}/frr_build")
    end #  Dir.chdir("#{o}/#{arch}/frr") do

    run "mkdir -p #{root}/etc/quagga"
    File.open("#{root}/etc/quagga/zebra.conf", 'w') do |f|
        f.puts("hostname Router")
        f.puts("password zebra")
        f.puts("enable password zebra")
        f.puts("interface lo")
        f.puts("interface sit0")
        f.puts("interface pimreg")
        f.puts("log file /tmp/zebra.log")
    end

    #if flags.include? :ospf
        File.open("#{root}/etc/quagga/ospfd.conf", 'w') do |f|
            f.puts("hostname ospfd")
            f.puts("password zebra")
            f.puts("log file /tmp/ospfd.log")
        end
    #end

    #if flags.include? :rip
        File.open("#{root}/etc/quagga/ripd.conf", 'w') do |f|
            f.puts("hostname ripd")
            f.puts("password zebra")
            f.puts("log file /tmp/ripd.log")
        end
    #end

    File.open("#{root}/etc/quagga/daemons", 'w') do |f|
        f.puts("zebra=yes")

    #if flags.include? :pim
        File.open("#{root}/etc/quagga/pimd.conf", 'w') do |f|
            f.puts("hostname pimd")
            f.puts("password zebra")
	    f.puts("interface pimreg")
            f.puts("log file /tmp/pimdd.log")
        end
    #end

        #if flags.include? :ospf
            f.puts("ospfd=yes")
        #else
         #   f.puts("ospfd=no")
        #end

        f.puts("bgpd=no")
        #f.puts("ospf6d=no")

        #if flags.include? :rip
            f.puts("ripd=yes")
        #else
         #   f.puts("ripd=no")
        #end

        f.puts("ripngd=no")
        f.puts("isisd=no")
    end
end

def clean_up o, arch
    root = "#{o}/#{arch}/root"
    run "rm -rf #{root}/usr/share"
    run "rm -rf #{root}/usr/include"
    run "rm -rf #{root}/usr/lib/*.a"
    run "rm -rf #{root}/usr/lib/*.la"
    run "rm -rf #{root}/etc/*.sample"
    run "rm -rf #{root}/usr/lib/pkgconfig"
    run "rm -rf #{root}/usr/sbin/frr-reload.py"
    run "rm -rf #{root}/usr/sbin/frr"
    run "rm -rf #{root}/usr/sbin/ssd"
    run "rm -rf #{root}/usr/sbin/rfptest"
    run "rm -rf #{root}/usr/bin/permutations"
end

def build o, arch, flags
    env = def_env arch, "#{o}/#{arch}/root"
    root = "#{o}/#{arch}/root"

    if $options[:no_remove_build] == false
        run "rm -rf #{o}/#{arch}"
    end

    run "mkdir -p #{o}/#{arch}"

    if $options[:is_build_c_ares] == true
        build_c_ares o, arch
    end

    if $options[:is_build_frr] == true
        build_frr o, arch, flags
    end

    if $options[:no_remove_build] == false
        clean_up o, arch
    end

    Dir.chdir("#{root}") do
        run "rm -rf ../root.tar ../root.squashfs"
        run "tar -cvzf ../root.tar *"
        run "#{env2str(env)} mksquashfs * ../root.squashfs -comp xz -all-root"
    end

    if $options[:no_remove_build] == false
        run "rm -rf #{root}"
        run "rm -rf #{o}/#{arch}/frr"
        run "rm -rf #{o}/#{arch}/frr_build"
        run "rm -rf #{o}/#{arch}/c-ares"
    end
end

run "rm -f status.json"

if $options[:no_remove_build] == false
    run "rm -rf ./build"
end

# Main build process
build "#{$base}/build/zebra_only", "mips", [:zebra]
build "#{$base}/build/all",        "mips", [:zebra, :ospf]

$git_sha = %x(git rev-parse --short HEAD).chop
$git_sha_long = %x(git rev-parse HEAD).chop
begin
  $git_id = run "git describe --tags --long"
rescue
  $git_id = $git_sha
end
if ENV['BRANCH_NAME']
  $git_branch = ENV['BRANCH_NAME']
else
  $git_branch = %x(git symbolic-ref --short -q HEAD).chop
end

$ver = "#{$src_conf["frr"][:version]}-#{$git_id}@#{$git_branch}"
$out_name = "frr-#{$ver}"

run "rm -rf *.tar.gz"
run "tar --transform \"s/^\./#{$out_name}/\" -cvzf #{$out_name}.tar.gz ./* ./.module_info"

$topRes = ResultNode.new('build', "OK",
                         { "version" => $ver,
                           "c-ares-version" => $src_conf["c-ares"][:version],
                           "frr-version" => $src_conf["frr"][:version]})

$topRes.reCalc
$topRes.to_file("status.json")
puts "Status: #{$topRes.status}"
exit 0 if $topRes.status == "OK"
$topRes.dump
exit -1

