+=begin
+
+ RegisterBinaryJob.rb
+
+Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+
+Contact:
+Taejun Ha <taejun.ha@samsung.com>
+Jiil Hyoun <jiil.hyoun@samsung.com>
+Donghyuk Yang <donghyuk.yang@samsung.com>
+DongHee Yang <donghee.yang@samsung.com>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Contributors:
+- S-Core Co., Ltd
+=end
+
+require "fileutils"
+$LOAD_PATH.unshift File.dirname(__FILE__)
+$LOAD_PATH.unshift File.dirname(File.dirname(__FILE__))+"/common"
+$LOAD_PATH.unshift File.dirname(File.dirname(__FILE__))+"/builder"
+$LOAD_PATH.unshift File.dirname(File.dirname(__FILE__))+"/pkg_server"
+require "client.rb"
+require "PackageManifest.rb"
+require "Version.rb"
+require "BuildServer.rb"
+require "JobLog.rb"
+require "mail.rb"
+require "utils.rb"
+require "ReverseBuildChecker.rb"
+
+class RegisterPackageJob
+
+ attr_accessor :id, :server, :pre_jobs, :os, :type
+ attr_accessor :status, :log, :source_path
+ attr_accessor :pkgsvr_client, :thread, :pkg_type
+ attr_accessor :pkg_name, :pkginfo, :cancel_state
+
+
+ # initialize
+ def initialize( local_path, project, server, ftpurl=nil )
+ @server = server
+ @id = server.jobmgr.get_new_job_id()
+ @log = nil
+ @type = "REGISTER"
+
+ @status = "JUST_CREATED"
+ @host_os = Utils::HOST_OS
+ @pkgserver_url = @server.pkgserver_url
+ @job_root = "#{@server.path}/jobs/#{@id}"
+ @source_path = @job_root+"/temp"
+ @job_working_dir=@job_root+"/works"
+ @buildroot_dir = "#{@job_root}/buildroot"
+ @cancel_state = "NONE"
+ @pre_jobs = []
+
+ @local_path=local_path
+ @file_path = nil
+ @filename = File.basename(local_path)
+ if @filename =~ /.*_.*_.*\.zip/ then
+ @pkg_type = "BINARY"
+ new_name = @filename.sub(/(.*)_(.*)_(.*)\.zip/,'\1,\2,\3')
+ @pkg_name = new_name.split(",")[0]
+ @pkg_version = new_name.split(",")[1]
+ @os = new_name.split(",")[2]
+ else
+ @pkg_type = "ARCHIVE"
+ @pkg_name = @filename
+ end
+ @pkginfo = nil #This info is valid only for BINARY package
+ @project = project
+ end
+
+
+ def is_sub_job?
+ return false
+ end
+
+
+ def get_project()
+ return @project
+ end
+
+
+ def get_buildroot()
+ return @buildroot_dir
+ end
+
+ def get_parent_job()
+ return nil
+ end
+
+
+ def is_rev_build_check_job()
+ return false
+ end
+
+ # execute
+ def execute(sync=false)
+ @log.info( "Invoking a thread for REGISTER Job #{@id}", Log::LV_USER)
+ if @status == "ERROR" then return end
+ @thread = Thread.new {
+ begin
+ thread_main()
+ terminate()
+ rescue => e
+ @log.error e.message
+ @log.error e.backtrace.inspect
+ end
+ }
+
+ if sync then
+ @thread.join
+ end
+
+ return true
+ end
+
+
+ #
+ def init
+ # mkdir
+ if not File.exist? @job_root then
+ FileUtils.mkdir_p @job_root
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ # create logger
+ if @log.nil? then
+ @log = JobLog.new(self, nil )
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ @log.info( "Initializing job...", Log::LV_USER)
+
+ # create dummy source path
+ if not File.exist? @source_path then
+ FileUtils.mkdir_p @source_path
+ end
+
+ # copy package file to source path
+ @file_path = "#{@source_path}/#{File.basename(@local_path)}"
+ if not File.exist? @local_path then
+ @log.error( "File not found!", Log::LV_USER)
+ @status = "ERROR"
+ return false
+ else
+ if not @project.nil? then
+ # if remote upload remove file and its directory
+ FileUtils.mv(@local_path, @file_path)
+ FileUtils.rm_rf("#{File.dirname(@local_path)}")
+ else
+ FileUtils.cp(@local_path, @file_path)
+ end
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ # set up pkgsvr_client
+ @pkgsvr_client = Client.new(@pkgserver_url, @job_working_dir, @log)
+
+ if @cancel_state != "NONE" then return false end
+
+ # check if the os is supported by build server
+ if @pkg_type == "BINARY" and
+ not @server.supported_os_list.include? @os then
+ @log.error( "Unsupported OS \"#{@os}\" is used!", Log::LV_USER)
+ @status = "ERROR"
+ return false
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ # checking version if not reverse-build job
+ if @pkg_type == "BINARY" then
+ # extrac pkg file
+ cmd = "cd \"#{@source_path}\";unzip #{@file_path}"
+ if not Utils.execute_shell(cmd) then
+ @log.error( "Extracting package file failed!", Log::LV_USER)
+ @status = "ERROR"
+ return false
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ # set up pkg info
+ begin
+ @pkginfo = PackageManifest.new("#{@source_path}/pkginfo.manifest")
+ rescue => e
+ @log.error( e.message, Log::LV_USER)
+ @status = "ERROR"
+ return false
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ if not check_package_version() then
+ @status = "ERROR"
+ return false
+ end
+ end
+
+ if @cancel_state != "NONE" then return false end
+
+ return true
+ end
+
+
+ #terminate
+ def terminate()
+ # report error
+ if @status == "ERROR" then
+ @log.error( "Job is stopped by ERROR" , Log::LV_USER)
+ @server.cleaner.clean_afterwards(@id)
+ else
+ # clean up
+ @server.cleaner.clean(@id)
+ if not @project.nil? then
+ @project.set_log_cnt( @log.cnt )
+ @project.write_ext_info
+ end
+ end
+
+ # close logger
+ @log.close
+ end
+
+
+ #cancel
+ def cancel()
+ if not @log.nil? then
+ @log.info( "JOB is canceled by cancel operation !!", Log::LV_USER)
+ end
+ end
+
+
+ # check building is possible
+ def can_be_built_on?(host_os)
+ return true
+ end
+
+
+ def get_packages()
+ if @pkg_type == "BINARY" then
+ return @pkginfo.packages
+ else
+ return []
+ end
+ end
+
+
+ def get_build_dependencies(target_os)
+ return []
+ end
+
+
+ def get_source_dependencies(target_os,host_os)
+ return []
+ end
+
+
+ def is_compatible_with?(o)
+ return false
+ end
+
+
+ def has_build_dependency?(other_job)
+ if has_same_packages?(other_job) or
+ does_depended_by?(other_job) then
+
+ return true
+ else
+ return false
+ end
+ end
+
+
+ def has_same_packages?( wjob )
+ if @type != wjob.type then return false end
+
+ case @pkg_type
+ when "BINARY"
+ if @pkg_name == wjob.pkg_name and
+ @os == wjob.os then
+ return true
+ end
+ when "ARCHIVE"
+ if @pkg_name == wjob.pkg_name then return true end
+ end
+
+ return false
+ end
+
+
+ # binary/archive package should not have build-dependencies
+ def does_depend_on?( wjob )
+ return false
+ end
+
+
+ def does_depended_by?( wjob )
+ if @pkg_type == "BINARY" then
+ wjob.get_build_dependencies(wjob.os).each do |dep|
+ # dep package of working job must have same name and target os
+ # with packages in my job
+ if dep.package_name == @pkg_name and
+ dep.target_os_list.include? @os then
+ #puts "Checking... A <- B"
+ return true
+ end
+ end
+ else
+ wjob.get_source_dependencies(wjob.os,@host_os).each do |dep|
+ if dep.package_name == @pkg_name then
+ return true
+ end
+ end
+ end
+
+ return false
+ end
+
+
+ def is_connected?
+ return true
+ end
+
+
+ # return the job is asyncronous job
+ def is_asynchronous_job?
+ return false
+ end
+
+ # set logger
+ def set_logger( logger )
+ @log = logger
+ end
+
+
+ def progress
+ if not @log.nil? then
+ if @project.nil? or @project.get_latest_log_cnt.nil? then
+ return "--% (#{log.cnt.to_s} lines) "
+ else
+ return ( ( @log.cnt * 100 ) / @project.get_latest_log_cnt ).to_s + "%"
+ end
+ end
+ # if log is nil then can't figure progress out
+ return ""
+ end
+
+
+ def get_log_url()
+ # only when server support log url
+ if @server.job_log_url.empty? then
+ return "",""
+ end
+
+ return "#{@server.job_log_url}/#{@id}/log",""
+ end
+
+ #
+ # PROTECTED METHODS
+ #
+ protected
+
+
+ # main module
+ def thread_main
+ @log.info( "New Job #{@id} is started", Log::LV_USER)
+
+ # clean build
+ if not ReverseBuildChecker.check( self, true ).empty? then
+ @status = "ERROR"
+ @log.error( "Reverse-build-check failed!" )
+ return
+ end
+
+ # if this package has compatible OS, check
+ if @pkg_type == "BINARY" and
+ @pkginfo.packages[0].os_list.count > 1 then
+
+ pkg = @pkginfo.packages[0]
+ pkg.os_list.each do |os|
+ if @os == os then next end
+
+ # skip when the os does not exist in project's supported os list
+ if not @project.nil? and not @project.os_list.include? os then next end
+
+ # skip when there is higher version of the package
+ ver_svr = @pkgsvr_client.get_attr_from_pkg( pkg.package_name, @os, "version")
+ if not ver_svr.nil? and
+ Version.new(@pkg_version) <= Version.new(ver_svr) then next end
+
+ # make new package file for compatible OS
+ newfile = "#{@pkg_name}_#{@pkg_version}_#{os}.zip"
+ @log.info( "Copying #{@filename} to #{newfile}" )
+ FileUtils.cp(@file_path,"#{@source_path}/#{newfile}")
+
+ # reverse check
+ if not ReverseBuildChecker.check( self, true, os ) then
+ @status = "ERROR"
+ @log.error( "Reverse-build-check failed!" )
+ return
+ end
+ end
+ end
+
+ # upload
+ if not upload() then
+ @status = "ERROR"
+ return
+ end
+
+ # INFO. don't change this string
+ @log.info( "Job is completed!", Log::LV_USER)
+ @status = "FINISHED"
+ end
+
+
+ # build projects that dependes on me
+ # can ignore some projects
+ def check_reverse_build( target_os )
+ @log.info( "Checking reverse build dependency ...", Log::LV_USER)
+
+ # get reverse-dependent projects
+ rev_pkgs = []
+ if @pkg_type == "BINARY" then
+ rev_pkgs += @pkgsvr_client.get_reverse_build_dependent_packages(@pkg_name, target_os)
+ else
+ rev_pkgs += @pkgsvr_client.get_reverse_source_dependent_packages(@pkg_name)
+ end
+
+ rev_projects = @server.prjmgr.get_projects_from_pkgs(rev_pkgs)
+
+ # create reverse build job
+ rev_build_jobs = []
+ rev_projects.each do |p|
+ prj = p[0]
+ os = p[1]
+ version = p[2]
+
+ if prj.type != "GIT" then next end
+
+ # create sub jobs for checking
+ new_job = prj.create_new_job_from_version(os, version)
+ new_job.set_rev_build_check_job(self)
+
+ rev_build_jobs.push new_job
+ end
+
+ # reverse build
+ if rev_build_jobs.count > 0 then
+ rev_prjs_txt = rev_build_jobs.map {|j| "#{j.get_project().name}(#{j.os})"}.join(", ")
+ @log.info( " * Will check reverse-build for next projects: #{rev_prjs_txt}", Log::LV_USER)
+ end
+ rev_build_jobs.each do |new_job|
+ @log.info( " * Checking reverse-build ... #{new_job.get_project().name}(#{new_job.id})", Log::LV_USER)
+ # job init
+ result = new_job.init()
+ # if init is succeeded!, try to execute
+ if result then
+ # check available server
+ rserver = @server.get_available_server( new_job )
+ if rserver != nil and rserver != @server then
+ new_job.set_remote_job( rserver )
+ end
+ # execute
+ new_job.execute(true)
+ if new_job.status == "ERROR" then result = false end
+ end
+
+ # check result
+ if not result then
+ return false
+ end
+ end
+
+ return true
+ end
+
+
+ def upload()
+ @log.info( "Uploading ...", Log::LV_USER)
+
+ # get package path list
+ if @pkg_type == "ARCHIVE" then
+ binpkg_path_list = Dir.glob("#{@source_path}/#{@pkg_name}")
+ else
+ binpkg_path_list = Dir.glob("#{@source_path}/*_*_*.zip")
+ end
+
+ # upload
+ u_client = Client.new( @server.pkgserver_url, nil, @log )
+ snapshot = u_client.upload( @server.pkgserver_addr, @server.pkgserver_port, @server.ftp_addr, @server.ftp_port, @server.ftp_username, @server.ftp_passwd, binpkg_path_list)
+
+ if snapshot.nil? then
+ @log.info( "Upload failed...", Log::LV_USER)
+
+ return false
+ end
+
+ # update local
+ @log.info( "Upload succeeded. Sync local pkg-server again...", Log::LV_USER)
+ @pkgsvr_client.update
+ @log.info("Snapshot: #{snapshot}", Log::LV_USER)
+
+ return true
+ end
+
+
+ # check if local package version is greater than server
+ def check_package_version()
+ @log.info( "Checking package version ...", Log::LV_USER)
+
+ # package update
+ @pkgsvr_client.update
+
+ @pkginfo.packages.each do |pkg|
+ ver_local = pkg.version
+ #ver_svr = @pkgsvr_client.get_package_version( pkg.package_name, @os )
+ ver_svr = @pkgsvr_client.get_attr_from_pkg( pkg.package_name, @os, "version")
+ if not ver_svr.nil? and Version.new(ver_local) <= Version.new(ver_svr) then
+ @log.error( "Version must be increased : #{ver_local} <= #{ver_svr}", Log::LV_USER)
+ return false
+ end
+ end
+
+ return true
+ end
+end