From 8d4be1b51a6004a0e00159632b37aec8177b221f Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 4 Aug 2013 15:39:01 -0400 Subject: [PATCH] add/update YARD documentation --- lib/rscons.rb | 1 + lib/rscons/builder.rb | 10 ++++ lib/rscons/builders/object.rb | 2 + lib/rscons/builders/program.rb | 2 + lib/rscons/cache.rb | 96 +++++++++++++++++++++++----------- lib/rscons/environment.rb | 52 ++++++++++++++++-- lib/rscons/monkey/module.rb | 2 + lib/rscons/monkey/string.rb | 7 +++ lib/rscons/varset.rb | 22 ++++++++ lib/rscons/version.rb | 1 + 10 files changed, 160 insertions(+), 35 deletions(-) diff --git a/lib/rscons.rb b/lib/rscons.rb index 411c642..270cde3 100644 --- a/lib/rscons.rb +++ b/lib/rscons.rb @@ -11,6 +11,7 @@ require "rscons/monkey/string" require "rscons/builders/object" require "rscons/builders/program" +# Namespace module for rscons classes module Rscons DEFAULT_BUILDERS = [ Object, diff --git a/lib/rscons/builder.rb b/lib/rscons/builder.rb index 99f0305..1b77f20 100644 --- a/lib/rscons/builder.rb +++ b/lib/rscons/builder.rb @@ -1,8 +1,18 @@ module Rscons + # Class to hold an object that knows how to build a certain type of file. class Builder + # Return a set of default variable values for the Environment to use + # unless the user overrides any. + # @param env [Environment] The Environment. def default_variables(env) {} end + + # Return whether this builder object is capable of producing a given target + # file name from a given source file name. + # @param target [String] The target file name. + # @param source [String, Array] The source file name(s). + # @param env [Environment] The Environment. def produces?(target, source, env) false end diff --git a/lib/rscons/builders/object.rb b/lib/rscons/builders/object.rb index 592b5a4..aab6a57 100644 --- a/lib/rscons/builders/object.rb +++ b/lib/rscons/builders/object.rb @@ -1,4 +1,6 @@ module Rscons + # A default RScons builder which knows how to produce an object file from + # various types of source files. class Object < Builder def default_variables(env) { diff --git a/lib/rscons/builders/program.rb b/lib/rscons/builders/program.rb index 48933b9..3479b60 100644 --- a/lib/rscons/builders/program.rb +++ b/lib/rscons/builders/program.rb @@ -1,4 +1,6 @@ module Rscons + # A default RScons builder that knows how to link object files into an + # executable program. class Program < Builder def default_variables(env) { diff --git a/lib/rscons/cache.rb b/lib/rscons/cache.rb index a336ca2..c7d91e7 100644 --- a/lib/rscons/cache.rb +++ b/lib/rscons/cache.rb @@ -5,46 +5,55 @@ require 'set' require 'rscons/version' module Rscons + # The Cache class keeps track of file checksums, build target commands and + # dependencies in a YAML file which persists from one invocation to the next. # Example cache: - # { - # version: '1.2.3', - # targets: { - # 'program' => { - # 'checksum' => 'A1B2C3D4', - # 'command' => ['gcc', '-o', 'program', 'program.o'], - # 'deps' => [ - # { - # 'fname' => 'program.o', - # 'checksum' => '87654321', - # } - # ], - # } - # 'program.o' => { - # 'checksum' => '87654321', - # 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'], - # 'deps' => [ - # { - # 'fname' => 'program.c', - # 'checksum' => '456789ABC', - # }, - # { - # 'fname' => 'program.h', - # 'checksum' => '7979764643', - # } - # ] + # { + # version: '1.2.3', + # targets: { + # 'program' => { + # 'checksum' => 'A1B2C3D4', + # 'command' => ['gcc', '-o', 'program', 'program.o'], + # 'deps' => [ + # { + # 'fname' => 'program.o', + # 'checksum' => '87654321', + # } + # ], + # } + # 'program.o' => { + # 'checksum' => '87654321', + # 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'], + # 'deps' => [ + # { + # 'fname' => 'program.c', + # 'checksum' => '456789ABC', + # }, + # { + # 'fname' => 'program.h', + # 'checksum' => '7979764643', + # } + # ] + # } # } # } - # } class Cache - # Constants + #### Constants + + # Name of the file to store cache information in CACHE_FILE = '.rsconscache' - # Class Methods + #### Class Methods + + # Remove the cache file def self.clear FileUtils.rm_f(CACHE_FILE) end - # Instance Methods + #### Instance Methods + + # Create a Cache object and load in the previous contents from the cache + # file. def initialize @cache = YAML.load(File.read(CACHE_FILE)) rescue { targets: {}, @@ -53,12 +62,30 @@ module Rscons @lookup_checksums = {} end + # Write the cache to disk to be loaded next time. def write File.open(CACHE_FILE, 'w') do |fh| fh.puts(YAML.dump(@cache)) end end + # Check if a target is up to date + # @param target [String] The name of the target file. + # @param command [Array] The command used to build the target. + # @param deps [Array] List of the target's dependency files. + # @param options [Hash] Optional options. Can contain the following keys: + # :strict_deps:: + # Only consider a target up to date if its list of dependencies is + # exactly equal (including order) to the cached list of dependencies + # @return true value if the target is up to date, meaning that: + # - the target exists on disk + # - the cache has information for the target + # - the command used to build the target is the same as last time + # - all dependencies listed are also listed in the cache, or, if + # :strict_deps was given in options, the list of dependencies is + # exactly equal to those cached + # - each cached dependency file's current checksum matches the checksum + # stored in the cache file def up_to_date?(target, command, deps, options = {}) # target file must exist on disk return false unless File.exists?(target) @@ -84,6 +111,10 @@ module Rscons end.all? end + # Store cache information about a target built by a builder + # @param target [String] The name of the target. + # @param command [Array] The command used to build the target. + # @param deps [Array] List of dependencies for the target. def register_build(target, command, deps) @cache[:targets][target] = { command: command, @@ -100,10 +131,15 @@ module Rscons # Private Instance Methods private + # Return a file's checksum, or the previously calculated checksum for + # the same file + # @param file [String] The file name. def lookup_checksum(file) @lookup_checksums[file] || calculate_checksum(file) end + # Calculate and return a file's checksum + # @param file [String] The file name. def calculate_checksum(file) @lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file)).encode(__ENCODING__) rescue '' end diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index ede0b2e..8f09829 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -2,14 +2,17 @@ require 'set' require 'fileutils' module Rscons + # The Environment class is the main programmatic interface to RScons. It + # contains a collection of construction variables, options, builders, and + # rules for building targets. class Environment + # [Array] of {Builder} objects. attr_reader :builders - # Initialize a newly constructed Environment object - # === Arguments - # +variables+ _Hash_ :: - # the variables hash can contain both construction variables, which are - # uppercase strings (such as "CC" or "LDFLAGS"), and rscons options, + # Create an Environment object. + # @param variables [Hash] + # The variables hash can contain both construction variables, which are + # uppercase strings (such as "CC" or "LDFLAGS"), and RScons options, # which are lowercase symbols (such as :echo). def initialize(variables = {}) @varset = VarSet.new(variables) @@ -36,6 +39,10 @@ module Rscons end end + # Make a copy of the Environment object. + # The cloned environment will contain a copy of all environment options, + # construction variables, builders, and build directories. It will not + # contain a copy of the targets. def clone(variables = {}) env = Environment.new() @builders.each do |builder_name, builder| @@ -54,6 +61,7 @@ module Rscons env end + # Add a {Builder} object to the Environment. def add_builder(builder) @builders[builder.class.short_name] = builder var_defs = builder.default_variables(self) @@ -64,10 +72,16 @@ module Rscons end end + # Specify a build directory for this Environment. + # Source files from src_dir will produce object files under obj_dir. def build_dir(src_dir, obj_dir) @build_dirs[src_dir.gsub('\\', '/')] = obj_dir.gsub('\\', '/') end + # Return the file name to be built from source_fname with suffix suffix. + # This method takes into account the Environment's build directories. + # It also creates any parent directories needed to be able to open and + # write to the output file. def get_build_fname(source_fname, suffix) build_fname = source_fname.set_suffix(suffix).gsub('\\', '/') @build_dirs.each do |src_dir, obj_dir| @@ -77,26 +91,37 @@ module Rscons build_fname end + # Access a construction variable or environment option. + # @see VarSet#[] def [](*args) @varset.send(:[], *args) end + # Set a construction variable or environment option. + # @see VarSet#[]= def []=(*args) @varset.send(:[]=, *args) end + # Add a set of construction variables or environment options. + # @see VarSet#append def append(*args) @varset.send(:append, *args) end + # Return a list of target file names def targets @targets.keys end + # Return a list of sources needed to build target target. def target_sources(target) @targets[target][:source] rescue nil end + # Build all target specified in the Environment. + # When a block is passed to Environment.new, this method is automatically + # called after the block returns. def process cache = Cache.new targets_processed = Set.new @@ -123,10 +148,23 @@ module Rscons cache.write end + # Build a command line from the given template, resolving references to + # variables using the Environment's construction variables and any extra + # variables specified. + # @param command_template [Array] template for the command with variable + # references + # @param extra_vars [Hash, VarSet] extra variables to use in addition to + # (or replace) the Environment's construction variables when building + # the command def build_command(command_template, extra_vars) @varset.merge(extra_vars).expand_varref(command_template) end + # Execute a builder command + # @param short_desc [String] Message to print if the Environment's :echo + # mode is set to :short + # @param command [Array] The command to execute. + # @param options [Hash] Optional options to pass to {Kernel#system}. def execute(short_desc, command, options = {}) print_command = proc do puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ') @@ -159,6 +197,10 @@ module Rscons end end + # Parse dependencies for a given target from a Makefile. + # This method is used internally by RScons builders. + # @param mf_fname [String] File name of the Makefile to read. + # @param target [String] Name of the target to gather dependencies for. def parse_makefile_deps(mf_fname, target) deps = [] buildup = '' diff --git a/lib/rscons/monkey/module.rb b/lib/rscons/monkey/module.rb index eda3d0e..36ce59e 100644 --- a/lib/rscons/monkey/module.rb +++ b/lib/rscons/monkey/module.rb @@ -1,4 +1,6 @@ +# Standard Ruby Module class. class Module + # @return the base module name (not the fully qualified name) def short_name name.split(':').last end diff --git a/lib/rscons/monkey/string.rb b/lib/rscons/monkey/string.rb index 3772071..7ef76f9 100644 --- a/lib/rscons/monkey/string.rb +++ b/lib/rscons/monkey/string.rb @@ -1,4 +1,8 @@ +# Standard Ruby String class. class String + # Check if the given string ends with any of the supplied suffixes + # @param suffix [String, Array] The suffix to look for. + # @return a true value if the string ends with one of the suffixes given. def has_suffix?(suffix) if suffix suffix = [suffix] if suffix.is_a?(String) @@ -6,6 +10,9 @@ class String end end + # Return a new string with the suffix (dot character and extension) changed + # to the given suffix. + # @param suffix [String] The new suffix. def set_suffix(suffix = '') sub(/\.[^.]*$/, suffix) end diff --git a/lib/rscons/varset.rb b/lib/rscons/varset.rb index 56be3a0..a97f9d2 100644 --- a/lib/rscons/varset.rb +++ b/lib/rscons/varset.rb @@ -1,11 +1,23 @@ module Rscons + # This class represents a collection of variables which can be accessed + # as certain types class VarSet + # The underlying hash attr_reader :vars + # Create a VarSet + # @param vars [Hash] Optional initial variables. def initialize(vars = {}) @vars = vars end + # Access the value of variable as a particular type + # @param key [String, Symbol] The variable name. + # @param type [Symbol, nil] Optional specification of the type desired. + # If the variable is a String and type is :array, a 1-element array with + # the variable value will be returned. If the variable is an Array and + # type is :string, the first element from the variable value will be + # returned. def [](key, type = nil) val = @vars[key] if type == :array and val.is_a?(String) @@ -17,21 +29,31 @@ module Rscons end end + # Assign a value to a variable. + # @param key [String, Symbol] The variable name. + # @param val [Object] The value. def []=(key, val) @vars[key] = val end + # Add or overwrite a set of variables + # @param values [VarSet, Hash] New set of variables. def append(values) values = values.vars if values.is_a?(VarSet) @vars.merge!(values) self end + # Create a new VarSet object based on the first merged with other. + # @param other [VarSet, Hash] Other variables to add or overwrite. def merge(other = {}) VarSet.new(Marshal.load(Marshal.dump(@vars))).append(other) end alias_method :clone, :merge + # Replace "$" variable references in varref with the variables values, + # recursively. + # @param varref [String, Array] Value containing references to variables. def expand_varref(varref) if varref.is_a?(Array) varref.map do |ent| diff --git a/lib/rscons/version.rb b/lib/rscons/version.rb index 936d85b..66ebcf2 100644 --- a/lib/rscons/version.rb +++ b/lib/rscons/version.rb @@ -1,3 +1,4 @@ module Rscons + # gem version VERSION = "0.0.1" end