# = TITLE: # # Project # # = COPYING: # # Copyright (c) 2007 Psi T Corp. # # This file is part of the ProUtils' Ratch program. # # Ratch is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ratch 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ratch. If not, see . require 'facets/filelist' require 'ratch/metadata/information' module Ratch # = Project class # # The Project class contains three sets of metadata. The first is the # typical general information about a project --title, description, homepage, etc. # This information is essentially static. Once set, it will probably never vary. # The second is default packaging informaiton. These both come from the General base # class. The third set is default build information, which may vary well for various # packages types, as this information may need to be overridden for specific # platform builds and package formats. This information describes how to # pack this project into various packages. Hecne it does not belong to the Package # class itself. # # General class is the base class of both the Project and Package classes. # The class contains typical general information about a project/package, such # as title, description, homepage, etc. This information is essentially static. # Once set, it will probably will never vary. The class also contains *defaults* # for package informaiton --information that is often static, but may vary # for a partciular package platform or format. A good example is the platform # attribute. class Project < Information PROJECT_FILE = '{,meta/}{project,package,index}{.yaml,.yml,}' def self.load location = Dir.pwd # ??? file = Dir.glob(PROJECT_FILE, File::FNM_CASEFOLD).first if file data = YAML::load(File.open(file)) new(location, data) else raise LoadError, "project file required -- #{PROJECT_FILE}" end end # New Project. def initialize(location, data={}) @location = location super(data) end # Location is needed to calculate some conventional defaults. attr_accessor :location # General #------------------------------------------------------------------------ # The title of the project (free-form, defaults to name). attr_accessor :title do @title || ( name if respond_to?(:name) ) end # Subtitle is limited to 60 characters. attr_accessor :subtitle do @subtitle.to_s[0..59] end # Brief one-line description of the package (Max 80 chars.) attr_accessor :summary, :brief do if @summary @summary.to_s[0..79] else i = @description.index('.') || 79 i = 79 if i > 79 @description[0..i] end end # More detailed description of the package. attr_accessor :description # "Unix" name of the project/package. attr_accessor :name # "Unix" name of master project this "sub-project" may belong. # (Default is the same as name). attr_accessor :project do @project || name end # The date the project was started. attr_accessor :created # Copyright notice. attr_accessor :copyright do @copyright || "Copyright (c) #{Time.now.strftime('%Y')} #{author}" end # Distribution License. attr_accessor :license do @license || 'GPLv3' end # Slogan or "trademark" phrase. attr_accessor :slogan # General one-word software category. attr_accessor :category # Author(s) of this project. # (Usually in "name " format.) attr_accessor :author # Contact(s) (defaults to authors). # TODO Move to Variants? attr_accessor :contact do @contact || author end # Gerneral email address. attr_accessor :email # Official domain associated with this package. attr_accessor :domain # Project's homepage. attr_accessor :homepage # Project's development site. attr_accessor :development, :devsite # Internet address(es) to documentation pages. attr_accessor :documentation, :docs # Internet address(es) to downloadable packages. attr_accessor :download # Internet address for project wiki. attr_accessor :wiki # Project's mailing list. attr_accessor :userlist, :mailinglist, :list # Developer's mailing list. attr_accessor :devlist do @devlist || @userlist end # Returns a standard taguri id for the library and release. def project_taguri "tag:#{name}.#{domain},#{created}" # or released? end # # Version # #------------------------------------------------------------------------ # # # Version number (eg. '1.0.0'). # attr_accessor :version # # # Current version code name. # attr_accessor :codename # # # Build number can br set to an arbitrar number, or if set to true, # # it will defaults to a number based on current date-time. # # attr_accessor :buildno do # @buildno = Time.now.strftime("%y%m%d%H%M") if TrueClass === @buildno # @buildno # end # Content Classification #------------------------------------------------------------------------ # Files in this package that are executables. # These files must in the packages bin/ directory. # If left blank all bin/ files are included. attr_accessor :executable, :executables do return [@executable].flatten.compact if @executable exes = [] dir = File.join(location, 'bin') if File.directory?(dir) Dir.chdir(dir) do exes = Dir.glob('*') end end @executable = exes end # Library files in this package that are *public*. # This is akin to load_path but specifies specific files # that can be loaded from the outside --where as those # not listed are considerd *private*. # # NOTE: This is not enforced --and may never be. It # complicates library loading. Ie. how to distinguish public # loading from external loading. But it something that can be # consider more carfully in the future. For now it can serve # as an optional reference. attr_accessor :library, :libraries do [@library || 'lib/**/*'].flatten end # Location(s) of executables. attr_accessor :bin_path, :bin_paths # Root location(s) of libraries (used by Rolls). # If you plan to support Gems, this would be something like: # # 'lib/facets' # # If not, then the default ('lib') is nice b/c it means one less # layer in your project heirarchy. attr_accessor :lib_path, :lib_paths, :load_path, :load_paths do [@lib_path || 'lib'].flatten end # Traditional load path (used by RubyGems). # The default is 'lib', which is usually fine. attr_accessor :gem_path, :gem_paths do [@gem_path || 'lib'].flatten end # Security #------------------------------------------------------------------------ # Encryption digest type used. # (md5, sha1, sha128, sha256, sha512). attr_accessor :digest do @digest || 'md5' end # Public key file associated with this library. This is useful # for security purposes especially remote loading. [pubkey.pem] attr_accessor :public_key do @public_key || 'pubkey.pem' end # Private key file associated with this library. This is useful # for security purposes especially remote loading. [_privkey.pem] attr_accessor :private_key # @private_key || '_privkey.pem' # end # Source Management #------------------------------------------------------------------------ # Specify which verison control system is being used. # Sometimes this is autmatically detectable, but it # is better to specify it. # Specifices the type of revision control system used. # darcs, svn, cvs, etc. # Will try to determine which version control system is being used. attr_accessor :scm do return @scm unless @scm.nil? @scm = if File.directory?('.svn') 'svn' elsif File.directory?('_darcs') 'darcs' else false end end # Files that are tracked under revision control. # Default is all less standard exceptions. # '+' and '-' prefixes can be used to augment the list # rather than fully override it. attr_accessor :track, :scm_files # Internet address to source code repository. # (http://, ftp://, etc.) attr_accessor :repository, :repo # Changelog file. attr_accessor :changelog # Manifest file. Defaults to 'MANIFEST'. # (I like to put it in meta/MANIFEST, personally.) attr_accessor :manifest do @manifest ||= 'MANIFEST' end # Dependencies #------------------------------------------------------------------------ # Package inter-relationship data. Generally refered to as package # "dependencies", but also includes +recommendations+, +suggestions+, # +replacements+, +provisions+, and +build-dependencies+, as well # as a few other fields that set a package apart. #------------------------------------------------------------------------ # What other packages *must* this package have in order to function. attr_accessor :dependency, :dependencies do @dependency || [] end # What other packages *should* be used with this package. attr_accessor :recommend, :recommends, :recommendations do @recommend || [] end # What other packages *could* be useful with this package. attr_accessor :suggest, :suggests, :suggestions do @suggest || [] end # What other packages does this package conflict. attr_accessor :conflict, :conflicts do @conflict || [] end # What other packages does this package replace. attr_accessor :replace, :replaces, :replacements do @replace || [] end # What other package(s) does this package provide the same dependency fulfilment. # For example, a package 'bar-plus' might fulfill the same dependency criteria # as package 'bar', so 'bar-plus' is said to provide 'bar'. attr_accessor :provide, :provides, :provisions do @provide || [] end # Abirtary information about what might be needed to use this package. # This is strictly information for the end-user to consider. # Eg. "Fast graphics card" attr_accessor :requirement, :requirements do @requirement || [] end # What packages does this package need to build? (eg. 'rake', 'ratch', etc.) attr_accessor :build_dependency, :build_dependencies do @build_dependency || [] end # Abirtary information about what might be needed to build this package. attr_accessor :build_requirement, :build_requirements do @build_requirement || [] end # Packaging #------------------------------------------------------------------------ # Platform. The default is nil, which is considered cross-platform. # This tends to only change for special builds. # # TODO: if current? attr_accessor :platform # Architecture(s) this package can be run on: any, i386, i686, ppc, etc. # This is strictly informational and is inteded to indicate the possiblities, # not the particular platform this package runs on. #attr_accessor :arch, :architecture do # @arch || "any" #end # Script to run prior to build. No entry indicates no compilation. attr_accessor :compile # Packages that are intended to compile on install may need this. It is a list # of "extension scripts" which generate Makefiles for use in compilation. attr_accessor :extensions do [@extensions || Dir.glob(File.join(location, 'ext/**/extconf.rb'))].flatten.compact end # #validate "compile script not found" do # compile ? File.file?(compile) : true #end # Package name. This defaults to name, but is here b/c it may # vary under different packagings (deb vs. gem). attr_accessor :package do @package || ( name if respond_to?(:name) ) end # Generate documentation on installation? attr_accessor :document # Distribution #------------------------------------------------------------------------ # Files to be distributed in a package. Defaults to all files. # If an entry is a directory then all it's contents are also included. # This along with @exclude@ and @ignore@ is used to generate a manifest. attr_accessor :distribute, :include do [@distribute || '**/*'].flatten.compact end # File to exclude from package. This is usually more useful than # @distribute@, as it allows you to remove from all files, rather then # explicitly designate everything to be included. Exlcusions have priority # over dsitribute's inclusions. If an entry is a directory then all # it's contents are also excluded. attr_accessor :exclude do [@exclude].flatten.compact end # Files to generally ignore, mainly used for manifest collection. Ignore # has priority over @exclude@ and @distribute@. attr_accessor :ignore do @ignore || %w{ **/.svn _darcs .config .installed } end # Add version tiers to package? If true a package's lib/ and ext/ files # will be wrapped in a version folder. (This is specialized transfer rule.) #attr_accessor :tier # Manifest file. #def manifest # @manifest #||= Manifest.open #end # Set manifest file, which will load it. #def manifest=(file) # @manifest = file # @filelist = File.read_list(file) #Manifest.open(file) # return file #end # List of file included in a package. This is generated using # @distribute@, @exlude@ and @ignore@. def filelist @filelist ||= collect_files(true) end # Validate that the files in the manifest actually exist. #def validate_manifest # missing = [] # filelist.each do |f| # missing << f unless File.exist?(f) # end # unless missing.empty? # raise ValidationError, "manifest lists non-existent files -- " + missing.join(" ") # end #end private # Collect distribution files. def collect_files(with_dirs=false) files = FileList.new Dir.chdir(location) do files.include(*distribute) files.exclude(*exclude) files.exclude(*ignore) end files = files.to_a # TODO: is there a way to do this with FileList? unless with_dirs files = files.select{ |f| !File.directory?(f) } end return files end # Validation #------------------------------------------------------------------------ public # validate "location is required" do location end # validate "executables do not exist" do exes = [] dir = File.join(location, 'bin') if File.directory?(dir) Dir.chdir(dir) do exes = Dir.glob('*') end end (executables - exes).empty? end end end