# Tell Rake NOT to remove directory(ies) named 'core' CLEAN.exclude("core") module AsProject # The RemoteFileTask was created to resolve # external tool dependencies for relatively stable # tools like mtasc, swfmill and mxmlc # The intention is that common tools will have # RemoteFileTasks deployed with AsProject and will # include mac/win/unix targets so that users on # each platform will seamlessly download and # target these tools. class RemoteFileTask < Rake::TaskLib attr_accessor :name, :user_task, :remote_task_name, :win_url, :osx_url, :unix_url, :win_extracted_file, :osx_extracted_file, :unix_extracted_file, :osx_mounted_path def initialize(name=:remote_file_task, do_not_define=false) @name = name @user_task = nil @remote_task_name = name.to_s yield self if block_given? define unless do_not_define end def define define_user_task self end def user return @user end def define_user_task if(@user_task.nil?) if(remote_task_name.nil?) raise UsageError.new('RemoteFileTask created without a remote_task_name value...') end @user = PathFinder.new().user @user_task = @user.remote_file_task(remote_task_name, self) task name => remote_task_name end @user_task end def execute(params) user_task.execute(params) end def clean_path(path) define_user_task return user_task.clean_path(path) end end ################################### # Concrete instances returned from # User objects class AbstractRemoteFileTask include Archive::Tar attr_accessor :name, :url, :mounted_path, :extracted_file def initialize(name, user) @name = name @user = user yield self if block_given? define end def define @uri = URI.parse(url) downloaded_file = downloaded_file_path file downloaded_file do |f| get_remote_file(@uri, downloaded_file) end if(extracted_file_path != downloaded_file) if(!Rake::Task.task_defined?(extracted_file_path)) file extracted_file_path => downloaded_file do |f| unpack_downloaded_file(downloaded_file, extracted_file_path) File.chmod(0755, extracted_file_path) end end # else # File.chmod(0755, downloaded_file) # task @name => url end task @name => [extracted_file_path] end def extracted_file_path return File.join(@user.downloads, @name.to_s, extracted_file) end def downloaded_file_path parts = @uri.path.split('/') file = parts.pop return File.join(@user.downloads, @name.to_s, file) end def unpack_downloaded_file(file_name, expected_file) dir = File.dirname(file_name) if(!File.exists?(expected_file)) if(is_zip?(file_name)) unpack_zip(file_name, dir) elsif(is_targz?(file_name)) unpack_targz(file_name, dir) elsif(is_dmg?(file_name)) unpack_dmg(file_name, dir) elsif(!is_exe?(file_name)) raise UsageError.new("RemoteFileTask does not know how to unpack files of type: #{file_name}") end end end def unpack_zip(zip_file, dir) Zip::ZipFile::open(zip_file) do |zf| zf.each do |e| fpath = File.join(dir, e.name) FileUtils.mkdir_p(File.dirname(fpath)) zf.extract(e, fpath) end end end def unpack_targz(tgz_file, dir) tar = Zlib::GzipReader.new(File.open(tgz_file, 'rb')) Minitar.unpack(tar, dir) end # This probably NOT the correct way to do this, # But I don't know much about macs, osx or dmgs # and it seems to work... # IF you know a better way - PLEASE let me know def unpack_dmg(dmg_file, dir) # 1) Mount the dmg in place # 2) Recursively Copy it's contents to asproject_home # 3) Unmount the dmg if(mounted_path.nil?) raise StandardError.new('DMG file downloaded, but the RemoteFileTask needs a mounted_path in order to mount it') end puts "Creating mounted path task: #{File.exists?(mounted_path)}" if(!File.exists?(mounted_path)) system(%{hdiutil mount #{dmg_file}}) end resolver = TemplateResolver.new resolver.copy_files(mounted_path, dir, false) File.chmod(0755, extracted_file) system(%{hdiutil unmount #{mounted_path}}) end def is_exe?(file) return (file.split('.').pop == 'exe') end def is_zip?(file) return (file.split('.').pop == 'zip') end def is_targz?(file) parts = file.split('.') return (parts.pop == 'gz' && parts.pop == 'tar') end def is_gzip?(file) return (file.split('.').pop == 'gz') end def is_dmg?(file) return (file.split('.').pop == 'dmg') end def make_downloaded_dir(file) dir = File.dirname(file) File.makedirs(dir) end def remote_file_dir return File.join(@user.downloads, @name.to_s) end def remote_location return File.join(remote_file_dir, extracted_file) end def fetch(location, limit = 10) uri = URI.parse(location) # Download the file now to the downloads dir # If the file is an archive (zip, gz, tar, tar.gz, dmg), extract to # AsProject/remote_files/@name/@location # Check the location again... raise UsageError.new("The RemoteFileTask can only handle HTTP requests at this time, it seems you were trying: #{uri.scheme}") if uri.scheme != 'http' raise UsageError.new('HTTP redirect too deep') if limit == 0 response = nil t = Thread.new { response = Net::HTTP.get_response(uri.host, uri.path) } # TODO: Add actual bytesLoaded vs bytesTotal # To the output... while t.alive? print(".") $stdout.flush sleep(0.2) end if(response.is_a? Net::HTTPSuccess) return response elsif(response.is_a? Net::HTTPRedirection) return fetch(response['location'], limit - 1) else puts response.to_s return response.error! end end def get_remote_file(uri, target) if(!File.exists?(target)) puts "Fetching #{uri.to_s} now!" response = fetch(uri.to_s) File.makedirs(File.dirname(target)) # Store the downloaded file on disk File.open(target, 'wb') do |f| f.write(response.body) end puts "" puts "Downloaded #{(response.body.size / 1024).to_s} KB" puts "to: #{target}" puts "" end end def clean_path(path) if(path.index(' ')) return %{'#{path}'} end return path end def execute(params) # Execute the system call with params # Sometimes DOS wants a 'run' prefix # Sometimes OSX wants a 'open' prefix # Sometimes Cygwin wants a './' prefix # This method should be overridden in # subclasses sh %{#{clean_path(extracted_file_path)} #{params}} end end class WinRemoteFileTask < AbstractRemoteFileTask def clean_path(path) path = path.split('/').join(File::SEPARATOR) if(path.index(' ')) return %{"#{path}"} end return path end def execute(params) sh(%{"#{extracted_file_path}" #{params}}) end end class CygwinRemoteFileTask < AbstractRemoteFileTask def execute(params) sh %{#{clean_path(extracted_file_path)} #{params}} end end class OSXRemoteFileTask < AbstractRemoteFileTask end class UnixRemoteFileTask < AbstractRemoteFileTask end end