Rework builder features to be returned by Builder#features instead of taken into account by Builder#produces?

This commit is contained in:
Josh Holtrop 2017-06-08 11:07:01 -04:00
parent b7f609b7e8
commit 010607d3b0
6 changed files with 113 additions and 76 deletions

View File

@ -24,6 +24,14 @@ module Rscons
{} {}
end end
# Return a set of build features that this builder provides.
#
# @return [Array<String>]
# Set of build features that this builder provides.
def features
[]
end
# Create a BuildTarget object for this build target. # Create a BuildTarget object for this build target.
# #
# Builder sub-classes can override this method to manipulate parameters # Builder sub-classes can override this method to manipulate parameters
@ -47,35 +55,17 @@ module Rscons
# Return whether this builder object is capable of producing a given target # Return whether this builder object is capable of producing a given target
# file name from a given source file name. # file name from a given source file name.
# #
# @overload produces?(target, source, env) # @param target [String]
# # The target file name.
# @param target [String] # @param source [String]
# The target file name. # The source file name.
# @param source [String] # @param env [Environment]
# The source file name. # The Environment.
# @param env [Environment]
# The Environment.
#
# @overload produces?(options)
#
# @since 1.10.0
#
# @param options [Hash]
# Options.
# @option options [String] :target
# Target file name.
# @option options [String] :source
# Source file name.
# @option options [Environment] :env
# The Environment.
# @option options [Hash] :features
# Features that this builder must satisfy.
# See {Environment#register_builds}.
# #
# @return [Boolean] # @return [Boolean]
# Whether this builder object is capable of producing a given target # Whether this builder object is capable of producing a given target
# file name from a given source file name. # file name from a given source file name.
def produces?(options) def produces?(target, source, env)
false false
end end

View File

@ -62,16 +62,18 @@ module Rscons
# Return whether this builder object is capable of producing a given target # Return whether this builder object is capable of producing a given target
# file name from a given source file name. # file name from a given source file name.
# #
# @param options [Hash] # @param target [String]
# Options. # The target file name.
# @param source [String]
# The source file name.
# @param env [Environment]
# The Environment.
# #
# @return [Boolean] # @return [Boolean]
# Whether this builder object is capable of producing a given target # Whether this builder object is capable of producing a given target
# file name from a given source file name. # file name from a given source file name.
def produces?(options) def produces?(target, source, env)
target, source, env, features = options.values_at(:target, :source, :env, :features) target.end_with?(*env['OBJSUFFIX']) and
(not features[:shared]) and
target.end_with?(*env['OBJSUFFIX']) and
KNOWN_SUFFIXES.find do |compiler, suffix_var| KNOWN_SUFFIXES.find do |compiler, suffix_var|
source.end_with?(*env[suffix_var]) source.end_with?(*env[suffix_var])
end end

View File

@ -21,6 +21,14 @@ module Rscons
} }
end end
# Return a set of build features that this builder provides.
#
# @return [Array<String>]
# Set of build features that this builder provides.
def features
%w[shared]
end
# Create a BuildTarget object for this build target. # Create a BuildTarget object for this build target.
# #
# The build target filename is given a platform-dependent suffix if no # The build target filename is given a platform-dependent suffix if no
@ -61,7 +69,7 @@ module Rscons
suffixes = env.expand_varref(["${OBJSUFFIX}", "${LIBSUFFIX}"], vars) suffixes = env.expand_varref(["${OBJSUFFIX}", "${LIBSUFFIX}"], vars)
# Register builders to build each source to an object file or library. # Register builders to build each source to an object file or library.
env.register_builds(target, sources, suffixes, vars, env.register_builds(target, sources, suffixes, vars,
features: {shared: true}) features: %w[shared])
end end
# Run the builder to produce a build target. # Run the builder to produce a build target.

View File

@ -37,19 +37,29 @@ module Rscons
} }
end end
# Return a set of build features that this builder provides.
#
# @return [Array<String>]
# Set of build features that this builder provides.
def features
%w[shared]
end
# Return whether this builder object is capable of producing a given target # Return whether this builder object is capable of producing a given target
# file name from a given source file name. # file name from a given source file name.
# #
# @param options [Hash] # @param target [String]
# Options. # The target file name.
# @param source [String]
# The source file name.
# @param env [Environment]
# The Environment.
# #
# @return [Boolean] # @return [Boolean]
# Whether this builder object is capable of producing a given target # Whether this builder object is capable of producing a given target
# file name from a given source file name. # file name from a given source file name.
def produces?(options) def produces?(target, source, env)
target, source, env, features = options.values_at(:target, :source, :env, :features) target.end_with?(*env['OBJSUFFIX']) and
features[:shared] and
target.end_with?(*env['OBJSUFFIX']) and
KNOWN_SUFFIXES.find do |compiler, suffix_var| KNOWN_SUFFIXES.find do |compiler, suffix_var|
source.end_with?(*env[suffix_var]) source.end_with?(*env[suffix_var])
end end

View File

@ -236,15 +236,15 @@ module Rscons
# Suffix, including "." if desired. # Suffix, including "." if desired.
# @param options [Hash] # @param options [Hash]
# Extra options. # Extra options.
# @option options [Hash] :features # @option options [Array<String>] :features
# Builder features to be used for this build. See {#register_builds}. # Builder features to be used for this build. See {#register_builds}.
# #
# @return [String] # @return [String]
# The file name to be built from +source_fname+ with suffix +suffix+. # The file name to be built from +source_fname+ with suffix +suffix+.
def get_build_fname(source_fname, suffix, options = {}) def get_build_fname(source_fname, suffix, options = {})
build_fname = Rscons.set_suffix(source_fname, suffix).gsub('\\', '/') build_fname = Rscons.set_suffix(source_fname, suffix).gsub('\\', '/')
options[:features] ||= {} options[:features] ||= []
extra_path = options[:features][:shared] ? "/_shared" : "" extra_path = options[:features].include?("shared") ? "/_shared" : ""
found_match = @build_dirs.find do |src_dir, obj_dir| found_match = @build_dirs.find do |src_dir, obj_dir|
if src_dir.is_a?(Regexp) if src_dir.is_a?(Regexp)
build_fname.sub!(src_dir, "#{obj_dir}#{extra_path}") build_fname.sub!(src_dir, "#{obj_dir}#{extra_path}")
@ -516,10 +516,7 @@ module Rscons
converted = nil converted = nil
suffixes.each do |suffix| suffixes.each do |suffix|
converted_fname = get_build_fname(source, suffix) converted_fname = get_build_fname(source, suffix)
builder = @builders.values.find do |builder| if builder = find_builder_for(converted_fname, source, [])
builder_produces?(builder, target: converted_fname, source: source)
end
if builder
converted = run_builder(builder, converted_fname, [source], cache, vars) converted = run_builder(builder, converted_fname, [source], cache, vars)
return nil unless converted return nil unless converted
break break
@ -548,13 +545,16 @@ module Rscons
# Extra variables to pass to the builders. # Extra variables to pass to the builders.
# @param options [Hash] # @param options [Hash]
# Extra options. # Extra options.
# @option options [Hash] :features # @option options [Array<String>] :features
# Set of features the builder must provide. # Set of features the builder must provide. Each feature can be proceeded
# * :shared - builder builds a shared object/library # by a "-" character to indicate that the builder must /not/ provide the
# given feature.
# * shared - builder builds a shared object/library
# #
# @return [Array<String>] # @return [Array<String>]
# List of the output file name(s). # List of the output file name(s).
def register_builds(target, sources, suffixes, vars, options = {}) def register_builds(target, sources, suffixes, vars, options = {})
options[:features] ||= []
@registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] ||= Set.new
sources.map do |source| sources.map do |source|
if source.end_with?(*suffixes) if source.end_with?(*suffixes)
@ -563,14 +563,7 @@ module Rscons
output_fname = nil output_fname = nil
suffixes.each do |suffix| suffixes.each do |suffix|
attempt_output_fname = get_build_fname(source, suffix, features: options[:features]) attempt_output_fname = get_build_fname(source, suffix, features: options[:features])
builder = @builders.values.find do |builder| if builder = find_builder_for(attempt_output_fname, source, options[:features])
builder_produces?(
builder,
target: attempt_output_fname,
source: source,
features: options[:features])
end
if builder
output_fname = attempt_output_fname output_fname = attempt_output_fname
self.__send__(builder.name, output_fname, source, vars) self.__send__(builder.name, output_fname, source, vars)
@registered_build_dependencies[target] << output_fname @registered_build_dependencies[target] << output_fname
@ -955,31 +948,43 @@ module Rscons
tc: tc)) tc: tc))
end end
# Determine if a builder produces an output file of a given name with # Find a builder that meets the requested features and produces a target
# requested features. # of the requested name.
#
# @param target [String]
# Target file name.
# @param source [String]
# Source file name.
# @param features [Array<String>]
# See {#register_builds}.
#
# @return [Builder, nil]
# The builder found, if any.
def find_builder_for(target, source, features)
@builders.values.find do |builder|
features_met?(builder, features) and builder.produces?(target, source, self)
end
end
# Determine if a builder meets the requested features.
# #
# @param builder [Builder] # @param builder [Builder]
# The builder. # The builder.
# @param options [Hash] # @param features [Array<String>]
# Options for {Builder#produces?}. # See {#register_builds}.
# @option options [String] :target
# Target file name.
# @option options [String] :source
# Source file name.
# @option options [Hash] :features
# Builder features. See {#register_builds}.
# #
# @return [Boolean] # @return [Boolean]
# Whether the builder produces a file of the given name from the source # Whether the builder meets the requested features.
# given with the features given. def features_met?(builder, features)
def builder_produces?(builder, options) builder_features = builder.features
if builder.method(:produces?).arity == 3 features.all? do |feature|
builder.produces?(options[:target], options[:source], self) want_feature = true
else if feature =~ /^-(.*)$/
options = options.dup want_feature = false
options[:features] ||= {} feature = $1
options[:env] = self end
builder.produces?(options) builder_has_feature = builder_features.include?(feature)
want_feature ? builder_has_feature : !builder_has_feature
end end
end end

View File

@ -334,6 +334,28 @@ module Rscons
end end
end end
describe "#features_met?" do
it "returns true when the builder provides all requested features" do
builder = Struct.new(:features).new(%w[shared other])
expect(subject.__send__(:features_met?, builder, %w[shared])).to be_truthy
end
it "returns true when no features are requested" do
builder = Struct.new(:features).new([])
expect(subject.__send__(:features_met?, builder, [])).to be_truthy
end
it "returns false when a builder does not provide a requested feature" do
builder = Struct.new(:features).new(%w[shared other])
expect(subject.__send__(:features_met?, builder, %w[other2])).to be_falsey
end
it "returns false when a builder provides a feature that is not desired" do
builder = Struct.new(:features).new(%w[shared other])
expect(subject.__send__(:features_met?, builder, %w[-shared])).to be_falsey
end
end
describe ".parse_makefile_deps" do describe ".parse_makefile_deps" do
it 'handles dependencies on one line' do it 'handles dependencies on one line' do
expect(File).to receive(:read).with('makefile').and_return(<<EOS) expect(File).to receive(:read).with('makefile').and_return(<<EOS)