lib/reap/tasks.rb in reap-5.10.10 vs lib/reap/tasks.rb in reap-6.0.0
- old
+ new
@@ -1,13 +1,674 @@
+require 'reap/taskutils'
-require 'reap/task/fileperm'
-require 'reap/task/test'
-require 'reap/task/testext'
-require 'reap/task/rdoc'
-require 'reap/task/announce'
-require 'reap/task/package'
-require 'reap/task/publish'
-require 'reap/task/info'
-require 'reap/task/install'
-#require 'reap/task/noop'
-#require 'reap/webpublish'
+# Tasks is the namespace in which to define task generators.
+module Tasks
+
+ # Even task generators might use the task tools.
+
+ include Reap::TaskUtils
+
+ #--
+ # Help system has been deprecated for the time being.
+ #def self.help( name, text )
+ # @help ||= {}
+ # @help[name] = text
+ #end
+ #++
+
+ # Used to determine task generator availablity.
+ #--
+ # TODO This is kind of a weak point in the design, I think.
+ # And tasks themselves also need to have Rake's #needed? feature.
+ #++
+
+ def self.available
+ @available ||= {}
+ end
+
+ # Used to set task generator availablity condition.
+
+ def self.avail( name, &cond )
+ available[name] = cond
+ end
+
+ # == Announce
+ #
+ # The announce task is intended for sending out a nice
+ # formated email message to a mailing address, especially
+ # mailing-lists.
+ #
+ # Announcement specific settings:
+ #
+ # server Email server to route message.
+ # port Email server's port.
+ # domain Email server's domain name.
+ # account Email account name.
+ # type Login type, either plain, cram_md5 or login.
+ # secure Uses TLS security, true or false?
+ # to Email address to send announcemnt.
+ # from Email address sent from.
+ # subject Subject of email message.
+ # links Array of http links to related sites.
+ # file File that contains announcement message.
+ # memo Embedded announcement message.
+ # slogan Motto for you project.
+ #
+ # Inherited settings:
+ #
+ # title Project title.
+ # summary Brief one-line description.
+ # description Long description of project.
+ # homepage Project homepage web address.
+ #
+
+ def announce( name, &data )
+
+ require 'reap/class/announce.rb'
+
+ desc "Email project announcement" unless desc
+
+ task name do
+ data = data.to_openobject
+ data.title ||= master.title
+ data.version ||= master.version
+ data.from ||= master.email
+ Reap::Announce.new( data ).write_and_email
+ end
+
+ end
+
+ # == Doap
+ #
+ # This task generates an XML DOAP project file.
+ #
+ # DOAP is an XML/RTF format for describing a project. It contains
+ # much of the same information as Reap's ProjectInfo file, but is
+ # more suitable to RESTful interapplication communications, like
+ # RSS/Atom feeds --useful to project tracking sites.
+ #
+ # The task gets almost all of the required parametes from the root
+ # node (i.e. master) of ProjectInfo file.
+
+ def doap( name, &data )
+
+ require 'reap/class/doap.rb'
+
+ desc "Generate DOAP project file" unless desc
+
+ task name do
+ data = data.to_openobject
+ Reap::Doap.new( data.__merge__( master ) ).call
+ end
+
+ end
+
+ avail :doap do
+ ProjectInfo.exists?
+ end
+
+ # == Extract Tests
+ #
+ # This task scans every package script looking for sections of the form:
+ #
+ # =begin test
+ # ...
+ # =end
+ #
+ # With appropriate headers, it copies these sections to files in your
+ # project's test/ dir, which then can be run using the Reap test task.
+ # The exact directory layout of the files to be tested is reflected in
+ # the test directory. You can then use Reap's test task to run the tests.
+ #
+ # Specific Settings:
+ #
+ # dir Specify the test directory. Default is 'test'.
+ # files Specify files to extract. Default is 'lib/**/*.rb'.
+ #
+
+ def extest( name, &data )
+
+ require 'reap/class/extest.rb'
+
+ desc "Extract embedded unit tests from scripts" unless desc
+
+ task name do
+ data = data.to_openobject
+ data.trunk ||= master.trunk
+ Reap::ExTest.new( data ).extract
+ end
+
+ end
+
+ # == File
+
+ def file( name, &exe )
+
+ require 'reap/class/filer'
+
+ name, source = preq_from_name( name )
+
+ task name do
+ Reap::Filer.new( name, source, &exe ).build
+ end
+
+ end
+
+ # == Info
+ #
+ # Displays the ProjectInfo file. This task is of little use
+ # --it's easy enough to look at a ProjectInfo file with
+ # less or your favorite editor. Mainly this task serves as
+ # a development example.
+
+ def info( name )
+
+ desc "Display ProjectInfo file" unless desc
+
+ task name do
+ puts ProjectInfo.instance.info_stream
+ end
+
+ end
+
+ avail :info do
+ ProjectInfo.exists?
+ end
+
+ # == Installer
+ #
+ # The installer task generates a specialized
+ # install.rb script specifically for your
+ # project. It currently does not support c/c++
+ # ext/ compilation.
+ #
+ # template Template for installation. (See below)
+ # options Hash of variable options. Project name
+ # and version are automatically added this.
+ #
+ # The template is a special configuration for installation.
+ # It should be a literal string with each line containing
+ # four parts seprated by spaces.
+ #
+ # [type] [from] [glob] [to]
+ #
+ # There are five types: bin, lib, ext, etc (or conf) and data.
+ #
+ # The template use variables in the from of $name. These are filled
+ # in using the options hash.
+ #
+ # Here's a basic example of an entry:
+ #
+ # ins: !!installer
+ # template: |
+ # bin bin * .
+ # lib lib **/* $name/$version
+ #
+
+ def installer( name, &data )
+
+ require 'reap/class/installer'
+
+ desc "Generate install.rb script" unless desc
+
+ task name do
+ data = data.to_openobject
+ data.options[:name] ||= master.name
+ data.options[:version] ||= master.version
+ Reap::Installer.new( data ).compile_and_write
+ end
+
+ end
+
+ # == Count
+ #
+ # Count the file and lines of code in your project.
+ #
+ # dir The directory of scripts to count.
+ #
+
+ def count( name, &data )
+
+ desc "Line count project scripts" unless desc
+
+ task name do
+ data = data.to_openobject
+ incl = data.include || 'lib/**/*' # TODO maybe check trunk too
+
+ fc, l, c, t, bt, r, rb = 0, 0, 0, 0, false, 0, false
+ Dir.glob( incl ).each do |fname|
+ next unless fname =~ /.*rb/ # TODO should this be done?
+ fc += 1
+ File.open( fname ) do |f|
+ while line = f.gets
+ l += 1
+ next if line =~ /^\s*$/
+ case line
+ when /^=begin\s+test/
+ tb = true; t+=1
+ when /^=begin/
+ rb = true; r+=1
+ when /^=end/
+ r+=1 if !(rb or tb)
+ (rb = false; r+=1) if rb
+ (tb = false; t+=1) if tb
+ when /^\s*#/
+ r += 1
+ else
+ c+=1 if !(rb or tb)
+ r+=1 if rb
+ t+=1 if tb
+ end
+ end
+ end
+ end
+ s = l - c - r
+ puts "FILES: #{fc}, LINES: #{l}, CODE: #{c}, DOC: #{r}, TEST: #{t}, SPACE: #{s}"
+ end
+
+ end
+
+ # == Manifest
+ #
+ # Create a manifest file for the package. Presently it is a very simple
+ # md5 + filename manifest. In the future this will be exanded to build
+ # a varity of manifest formats.
+ #
+ # Task specific settings:
+ #
+ # include Files to include
+ # exclude Files to exclude from the included.
+ #
+
+ def manifest( name, &data )
+
+ require 'reap/class/manifest'
+
+ desc "Create a MANIFEST file for this package" unless desc
+
+ task name do
+ data = data.to_openobject
+ data.trunk ||= master.trunk
+ Reap::Manifest.new( data ).generate
+ end
+
+ end
+
+ # == Package
+ #
+ # This task creates standard .zip, .tgz, or .tbz
+ # packages, plus .gem or .deb distributions.
+ #
+ # Builds distribution packages. The package task supports
+ # tar.gz, tar.bz2, zip source packages and gem, pacman and
+ # debian ditribution packages.
+ #
+ # Task specific settings:
+ #
+ # dir Directory in which to store distributions.
+ # include Files to include in distribution.
+ # exclude Files to exclude from those.
+ # distribute List of distribution types desired. Eg.
+ # tgz, tar.gz, tbz, tar.bz2, zip
+ # gem, deb, pac
+ # project Project name.
+ # category Software category.
+ # architecture Can be any, i368, i686, ppc, etc.
+ # dependencies List of packages this program depends.
+ # recommends List of packages that can be used with this package.
+ # replaces List of packages this one replaces.
+ # executables Executable files in this distribution.
+ #
+ # RubyGems specific settings:
+ #
+ # autorequire
+ # platform
+ # require_paths
+ #
+ # The package task also has subsection for each type of distribution.
+ # These can be used to override settings in the package information
+ # if they in some way differ. Possible subsections are:
+ #
+ # gems
+ # pacman
+ # debian
+ #
+
+ def package( name, &data )
+
+ require 'reap/class/package'
+
+ desc "Create distribution packages" unless desc
+
+ task name do
+ data = data.to_openobject
+
+ data.name ||= master.name
+ data.title ||= master.title
+ data.version ||= master.version
+ data.status ||= master.status
+ data.series ||= master.series
+ data.author ||= master.author
+ data.maintainer ||= master.maintainer
+ data.email ||= master.email
+ data.summary ||= master.summary
+ data.architecture ||= master.architecture
+ data.license ||= master.license
+ data.project ||= master.project
+ data.homepage ||= master.homepage
+ data.trunk ||= master.trunk
+
+ Reap::Package.new( data ).generate_packages
+ end
+
+ end
+
+ # == Publish
+ #
+ # Publish documents to hosting service. Three means of
+ # publishing are current supported, ftp uploading, scp,
+ # and web convenience option that recognizes particular
+ # hosts (only RubyForge is currently supported).
+ #
+ # type Type of publishing, 'web', 'scp' or 'ftp'.
+ # host Host server, default is 'rubyforge.org'
+ # username Username for host.
+ # dir Directory of files to publish. Or,
+ # copy Files to publish using from and to.
+ # project Project name (used for Rubyforge)
+ #
+ # If the type is 'scp' or 'ftp' you will also need to provide
+ # the root directory parameter.
+ #
+ # root Document root directory at host.
+ #
+ # The dir parameter allows you to simply specify a local
+ # directory, the contents of which will be published to
+ # host's document root location (directory tree intact).
+ #
+ # If you need more control over which files to publish
+ # where, you can use the copy parameter instead. Provide
+ # an array of pattern strings in the form of "{from} {to}".
+ # If the desitination is the host's document root you do
+ # not need to specify the {to} part. For example:
+ #
+ # copy: [ 'web/*', 'doc/api/* doc/api' ]
+ #
+ # The first copies the files under your project's web directory
+ # to the host's document root. The second copies your projects
+ # doc/api files to the doc/api location on the host.
+ #
+ # When using the 'scp' type the internal template used for
+ # the outbound destination is 'username@host:root/'.
+
+ def publish( name, &data )
+
+ require 'reap/class/publish.rb'
+
+ desc "Publish documents to the web or host" unless desc
+
+ task name do
+ data = data.to_openobject
+
+ data.project ||= master.rubyforge.project || master.name
+ data.username ||= master.rubyforge.username
+
+ Reap::Publish.new( data ).publish
+ end
+
+ end
+
+ # == RDoc
+ #
+ # This task generates RDoc API documentation from source
+ # comments.
+ #
+ # Task specific settings:
+ #
+ # dir Directory to store documentation [doc].
+ # main File to use as main page of RDocs.
+ # template Which RDoc template to use.
+ # include Files to include in RDocs.
+ # exclude Files to exclude from those.
+ # options Pass-thru extra options to RDoc command.
+ #
+ # Inherited settings:
+ #
+ # title Project title to use in RDocs.
+ #
+
+ def rdoc( name, &data )
+
+ require 'reap/class/rdoc'
+
+ desc "Generate RDoc API documentation" unless desc
+
+ task name do
+ data = data.to_openobject
+
+ data.title ||= master.title
+ data.trunk ||= master.trunk
+
+ Reap::RDoc.new( data ).call
+ end
+
+ end
+
+ # == Release
+ #
+ # This task releases files to RubyForge --it should work with other
+ # GForge instaces or SourceForge clones too.
+ #
+ # While defaults are nice, you may want a little more control. You can
+ # specify additional attributes:
+ #
+ # dir Distribution directory
+ # exclude Distribution types to exclude
+ # host URL of host service
+ # username Username of host service
+ # project Project name at host
+ # package Package name
+ # date Date of release (defaults to Time.now)
+ # groupid Group id number
+ # processor Processor/Architecture (Any, i386, PPC, etc.)
+ # release Release name (default is "%s")
+ # is_public Public release?
+ # changelog Change log file
+ # notelog Release notes file
+ #
+ # The release option can be a template by using %s in the
+ # string. The version number of your project will be sub'd
+ # in for the %s. This saves you from having to update
+ # the release name before every release.
+
+ def release( name, &data )
+
+ require 'reap/class/release'
+
+ desc "Release distribution files" unless desc
+
+ task name do
+ data = data.to_openobject
+
+ data.version = master.version
+ data.project ||= master.rubyforge.project || master.name
+ data.username ||= master.rubyforge.username
+ data.package ||= master.rubyforge.package
+ data.groupid ||= master.rubyforge.groupid
+
+ Reap::Release.new( data ).release
+ end
+
+ end
+
+ # == Scaffold
+ #
+ # Generates a project directory layout within the current
+ # directory. The scaffolding includes all the standard
+ # features such as directories lib/ bin/ doc/
+ # and files such as README.
+ #
+ # To use this task you should first create an empty
+ # directory and than change into it. This command will
+ # not create a new project directory. As a safegaurd, this
+ # command will not generate scaffolding in a directory with
+ # files already present, unless you use the -f (force) option.
+ #
+ # There are currently two types of supported scaffoldings:
+ # 'standard', which is the default if no type is given,
+ # and 'subversion' which creates a "trunk" hierarchy.
+
+ def scaffold( name )
+
+ desc "Generate a project directory layout" unless desc
+
+ task name do |type|
+ content = Dir.entries('.') - [ '.', '..' ]
+ if not content.empty? and not $FORCE
+ tell "Directory already has content. Use -f option to force scaffolding."
+ return nil
+ end
+ type ||= 'standard'
+ type = 'standard' if type == 'std'
+ type = 'subversion' if type == 'svn'
+ # gems workaround
+ if dir = Gem.gempath('reap')
+ dir = File.join( dir, 'data', 'reap', 'scaffold', type )
+ else
+ dir = File.join( ::Config::CONFIG['datadir'], 'reap', 'scaffold', type )
+ end
+ #
+ unless File.directory?( dir )
+ tell "Unrecognized project type."
+ else
+ #FileUtils.mkdir_p( name )
+ FileUtils.cp_r( File.join( dir, '.'), '.' )
+ tell "Project ready."
+ end
+ end
+
+ end
+
+ avail :scaffold do
+ ! ProjectInfo.exists?
+ end
+
+ # == Setup
+ #
+ # This task manually installs a project using setup.rb.
+ # If setup.rb doesn't exist it will be created.
+
+ def setup( name, &data )
+
+ desc "Locally install package using setup.rb" unless desc
+
+ task name do
+ data = data.to_openobject
+
+ data.trunk ||= master.trunk || '.'
+ data.options ||= []
+
+ unless provide_setup_rb( data.trunk )
+ puts "Setup.rb is missing. Forced to cancel task."
+ return nil
+ end
+
+ puts "Reap is shelling out work to setup.rb..."
+
+ #--
+ # SHELL OUT! This will be fixed with swtich to Reap's installer instead of setup.rb.
+ #++
+ exe = %{ruby setup.rb}
+ exe << ' -q ' unless $VERBOSE
+ exe << data.options.join(' ')
+ exe << ' all'
+
+ success = false
+ Dir.chdir( data.trunk ) do
+ success = sh( exe )
+ end
+
+ puts "Setup complete!" if success
+ end
+
+ end
+
+ # == Template
+ #
+ # Creates an empty ProjectInfo file in the current directory.
+ # It will not overwrite a ProjectInfo file if one is already
+ # present. Unlike 'scaffold', this is especially useful for
+ # pre-existing projects.
+
+ def template( name )
+
+ require 'facet/gem/self/gempath'
+
+ desc "Create a ProjectInfo template" unless desc
+
+ task name do
+ f = nil
+ if ::ProjectInfo::INFO_FILES.any?{ |f| File.exists?(f) }
+ puts "Project file '#{f}' already exists."
+ return
+ end
+ filename = 'ProjectInfo'
+ # if using gems
+ if dir = Gem.gempath('reap')
+ dir = File.join( dir, 'data', 'reap', 'scaffold', 'standard' )
+ else
+ dir = File.join( ::Config::CONFIG['datadir'], 'reap', 'scaffold', 'standard' )
+ end
+ f = File.join( dir, filename )
+ raise "ProjectInfo template file #{f} is missing." unless File.file?( f )
+ # copy
+ FileUtils.install( f, '.' )
+ tell "#{filename} created. You'll need to fill it out."
+ end
+
+ end
+
+ avail :template do
+ ! ProjectInfo.exists?
+ end
+
+ # == Test
+ #
+ # The Reap test class runs each test in it's own
+ # proccess, making for a more pure test facility.
+ # Reap runs each test in a separate proccess to aviod
+ # potential conflicts between scripts.
+ #
+ # files Test files (eg. test/tc_**/*.rb)
+ # Defaults to typcial selection.
+ #
+ # libs List of lookup directories to include in
+ # load path. './lib' is always included.
+ #
+ # live Flag to quickly deactive use of local libs.
+ # Test against installed files instead.
+ #
+ # requires List of any files to pre-require.
+ #
+ # NOTE This works well enough but it is a tad delicate.
+ # It actually marshals test results across stdout->stdin
+ # shell pipe. One consequence of this is that you can't
+ # send debug info to stdout in your tests (including #p and #puts).
+
+ def test( name, &data )
+
+ require 'reap/class/test'
+
+ desc "Run unit-tests (each in a separate process)" unless desc
+
+ task name do
+ data = data.to_openobject
+
+ data.trunk ||= master.trunk
+
+ Reap::Test.new( data ).call
+ end
+
+ end
+
+end #module Tasks