require "active_support/ordered_hash" require "pipeline/coding/projbuild/config" require "pipeline/coding/projbuild/xml/include" require "pipeline/scm/perforce_file.rb" module Pipeline module ProjBuild module Info class ConfigPathInfo attr_reader :configs, :multiple_per_config def initialize( subpath ) @subpath = subpath @configs = Hash.new() @multiple_per_config = false tokens = subpath.split("/") if tokens.size > 1 then if XMLUtil::Include.instance().allow_count(tokens[tokens.size - 2],tokens.last) == -1 then @multiple_per_config = true end end end def add_path(config, path) if @configs[config] == nil then @configs[config] = Array.new() else @multiple_per_config = true end @configs[config] << path end def combine_attributes( config ) @combine_config = config parent_node = config.root_node tokens = @subpath.split("/") tokens.delete_at(0) if tokens[0].strip == "" name = nil if tokens.size != 1 then tokens.delete_at(0) #get rid of 'config' child_node = nil while(tokens.size > 1) child_node = parent_node.find_first(tokens[0]) if child_node == nil then child_node = XML::Node.new(tokens[0]) parent_node << child_node end parent_node = child_node tokens.delete_at(0) end name = tokens[0] end return combine_attributes_multi(parent_node,name) if (@multiple_per_config == true) set_node = parent_node if name != nil then set_node = XML::Node.new(name) parent_node << set_node end attribute_appearances = Hash.new() attribute_merge = Hash.new() @configs.keys.each { |key| xml_node = key.find_first(@subpath) xml_node_attributes = xml_node.attributes xml_node_attributes.each do |attribute| if attribute_appearances[attribute.name] == nil then attribute_appearances[attribute.name] = 1 attribute_merge[attribute.name] = attribute.value else attribute_appearances[attribute.name] = attribute_appearances[attribute.name] + 1 attribute_merge[attribute.name] = nil if attribute_merge[attribute.name] != attribute.value end end } attribute_merge.keys.each { |key| #make sure we have the attribute for every config next if @configs.keys.size != attribute_appearances[key] next if attribute_merge[key] == nil #add to root config's set_node set_node_attributes = set_node.attributes set_node_attributes[key] = attribute_merge[key] #remove from child configs' xml_nodes @configs.keys.each { |config| child_node = config.find_first(@subpath) child_node_attributes = child_node.attributes child_node_attributes.get_attribute(key).remove! } } end protected def combine_attributes_multi(parent_node, name) j = 1 to_delete = Array.new() config_subpaths = @configs.keys[0].find(@subpath) config_subpaths.each { |xml_node| config_indices = [j] 1.upto(@configs.keys.size - 1) { |i| k = 1 config_subpathsi = @configs.keys[i].find(@subpath) config_subpathsi.each { |xml_compare_node| attribute_count = 0 xml_node.attributes.each { |attribute| attribute_count = attribute_count + 1 if xml_compare_node.attributes[attribute.name] == attribute.value } if attribute_count == xml_node.attributes.length then config_indices << k break end k = k + 1 } config_subpathsi = nil GC.start } if config_indices.size == @configs.keys.size then already_exists = false combine_subpaths = @combine_config.find(@subpath) @combine_config.find(@subpath).each { |xml_compare_node| attribute_count = 0 xml_node.attributes.each { |attribute| attribute_count = attribute_count + 1 if xml_compare_node.attributes[attribute.name] == attribute.value } if attribute_count == xml_node.attributes.length then already_exists = true break end } combine_subpaths = nil if already_exists == false then set_node = XML::Node.new(name) get_node = @configs.keys[0].find_first("#{@subpath}[#{config_indices[0]}]") get_node.attributes.each { |attribute| set_node.attributes[attribute.name] = attribute.value } parent_node << set_node end 0.upto(@configs.keys.size - 1) { |k| get_node = @configs.keys[k].find_first("#{@subpath}[#{config_indices[k]}]") to_delete << get_node if get_node != nil } end j = j + 1 } config_subpaths = nil to_delete.each { |delete_node| delete_node.remove! } end end class ConfigMerger def initialize( config_array ) @paths = ActiveSupport::OrderedHash.new() @config_array = config_array.dup config_array.each { |config| add_node(config.xml_data,config) } end def combine( config ) @paths.keys.each { |key| #this stops paths being combined that are not on every config from @config_array next if @config_array.size != @paths[key].configs.size @paths[key].combine_attributes(config) } remove_blank(config.xml_data) @config_array.each { |config_arr| remove_blank(config_arr.xml_data) } end protected def remove_blank( xml_node ) xml_node.children.each { |child| remove_blank(child) child.remove! if (child.children? == false) and (child.attributes.length == 0) } end def add_node( xml_node, config ) subpath = xml_node.path.split("[")[0] if @paths[subpath] == nil then @paths[subpath] = ConfigPathInfo.new(subpath) end path_info = @paths[subpath] path_info.add_path(config,xml_node.path) xml_node.each_element { |node| add_node(node,config) } end end class Config attr_reader :xml_data def initialize() @xml_data = nil end def to_s() @xml_data.to_s end def find( xpath ) return nil if @xml_data == nil @xml_data.find(xpath) end def find_first( xpath ) return nil if @xml_data == nil @xml_data.find_first(xpath) end def xml_data=(x) @xml_data = x if @xml_data == nil then @xml_doc = nil else @xml_doc = XML::Document.new() @xml_doc.root = @xml_data end end def root_node() if @xml_data == nil then @xml_data = XML::Node.new("config") @xml_doc = XML::Document.new() @xml_doc.root = @xml_data end @xml_data end def is_null() return true if @xml_data == nil return false end def Config.merge( configparent, config ) return config if configparent == nil return config if configparent.xml_data == nil return configparent.dup if config == nil return configparent.dup if config.xml_data == nil ret = Config.new() ret.xml_data = configparent.xml_data.copy(true) XMLUtil::Include.instance().merge_node(ret.xml_data,config.xml_data) ret end def contract contract_node(@xml_data) end def Config.do_combine( config_list ) return nil if config_list == nil return nil if config_list.size == 0 return nil if config_list[0] == nil return nil if config_list[0].xml_data == nil config_array = Array.new() config_list.each { |config| config_array << config if config != nil } doc_ret = Config.new() merge = ConfigMerger.new(config_array) merge.combine(doc_ret) if doc_ret.xml_data.children? == false and doc_ret.xml_data.attributes.length == 0 then doc_ret = nil end #remove the config itself if there's nothing in it... config_list.each { |config| next if config == nil config.contract next if config.xml_data == nil if config.xml_data.children? == false and config.xml_data.attributes.length == 0 then config.xml_data = nil end } doc_ret end def Config.do_expansion( config_src, config_list ) end protected def contract_node( xml_node ) xml_node.children.each { |child_node| contract_node(child_node) } paths = Hash.new() xml_node.children.each { |child_node| subpath = child_node.path.split("[")[0] if paths[subpath] == nil paths[subpath] = 1 else paths[subpath] = paths[subpath] + 1 end } to_delete = Array.new() paths.keys.each { |path| if paths[path] > 1 then 0.upto(paths[path] - 1) { |i| (i + 1).upto(paths[path] - 1) { |j| xml_a = xml_node.find_first("#{path}[#{i + 1}]") xml_b = xml_node.find_first("#{path}[#{j + 1}]") to_delete << xml_b if xml_a == xml_b } } end } to_delete.each { |xml_delete| xml_delete.remove! if xml_delete != nil } if xml_node.children? == false and xml_node.attributes.length == 0 then @xml_data = nil if xml_node == @xml_data #xml_node.remove! end end end class Platform attr_reader :sym, :targets, :config attr_writer :config def to_s() ret = "platform: #{sym.to_s} config: #{@config}\ntargets:\n" @targets.keys.each { |target_sym| target_config = @targets[target_sym] ret = ret + "target: #{target_sym.to_s} #{target_config.to_s}\n" } ret end def initialize( sym, targets = nil ) @sym = sym @targets = ActiveSupport::OrderedHash.new() if targets != nil then targets.keys.each { |target| @targets[target] = Config.new() } end @config = Config.new() end def get_target( target_name ) @targets[target_name] = Config.new() if @targets[target_name] == nil @targets[target_name] end def contract() new_config = Config::do_combine(targets.values) if @config == nil then @config = new_config else @config = Config.merge(@config,new_config) end @config.contract if @config != nil @config = nil if @config != nil and @config.xml_data == nil targets.keys.each { |target_name| if @targets[target_name].xml_data == nil then @targets.delete(target_name) end } end end class Configurable attr_reader :platforms, :config def to_s() ret = "configurable: #{@config.to_s}\nplatforms:\n" @platforms.each { |platform_sym,platform| ret = ret + "#{platform.to_s}\n" } ret end def initialize() @config = Config.new() @platforms = ActiveSupport::OrderedHash.new() PlatformInfo.instance().platforms.keys.each { |platform_sym| @platforms[platform_sym] = Platform.new(platform_sym) } end def contract() platform_configs = ActiveSupport::OrderedHash.new() @platforms.each { |key,platform| platform.contract #remove a platform if there is nothing in it... if platform.targets.size == 0 and (platform.config == nil or platform.config.is_null) then @platforms.delete(key) next end return nil if platform.config == nil return nil if platform.config.xml_data == nil platform_configs[platform.sym] = platform.config } return nil if platform_configs.values.size < @platforms.size #At this point, every platform is used. Try to merge these configurations #into the base file configuration. new_config = Config::do_combine(platform_configs.values) if @config == nil then @config = new_config else @config = Config.merge(@config,new_config) end @platforms.values.each { |platform| platform.config = platform_configs[platform.sym] } @config.contract end end class File < Configurable attr_reader :path, :has_custom_build_steps attr_writer :path, :has_custom_build_steps #Cache a boolean here for custom build steps instead of re-evaluating the entire object with an XPATH object. attr_accessor :excluded_from_build def initialize( path ) super() @path = path @has_custom_build_steps = false @excluded_from_build = false end def to_s() ("file: " + @path + "\n" + super()) end end class Filter < Configurable attr_reader :name, :files, :filters, :path attr_writer :name, :files, :filters, :path def initialize( name ) super() @name = name @path = "" @files = Array.new() @filters = Array.new() end def expand() end def contract() #contract sub files filters.compact! files.compact! filters.each { |filter| filter.contract } files.each { |file| file.contract } #contract file paths... paths = Array.new() filters.each { |filter| paths << filter.path } files.each { |file| paths << OS::Path::remove_filename(file.path) } common_path = OS::Path::get_common(paths) new_path = OS::Path::combine(@path,common_path) if common_path != nil and common_path != "" @path = new_path if new_path != nil and ::File.directory?(new_path) == true if common_path.nil? == false then filters.each { |filter| filter.path.sub!(common_path, "") } files.each { |file| file.path.sub!(common_path, "") } end super() end def create_platforms_and_targets( platforms, targets ) @platforms = Hash.new() platforms.keys.each { |platform| @platforms[platform] = Platform.new(platform,targets) } end end class Project < Filter attr_reader :guid, :dep_guids attr_writer :guid, :dep_guids def initialize( name ) super(name) @guid = "" @dep_guids = Array.new() end def rationalise() self.expand self.contract end end end #module User class ProjectConfig attr_reader :guid, :platform, :target def initialize( guid, platform, target ) @guid = guid @platform = platform @target = target end end class ProjBuildData include Singleton attr_reader :projects, :dependent_projects, :root_guid, :sln_platforms attr_writer :root_guid, :sln_platforms def initialize() reset() end def reset() @root_guid = nil @projects = ActiveSupport::OrderedHash.new() @dependent_projects = ActiveSupport::OrderedHash.new() @sln_platforms = ActiveSupport::OrderedHash.new() end def add_projectconfig( sln_platform, sln_target, project_guid, proj_platform, proj_target ) if @sln_platforms[sln_platform] == nil then @sln_platforms[sln_platform] = ActiveSupport::OrderedHash.new() end if @sln_platforms[sln_platform][sln_target] == nil then @sln_platforms[sln_platform][sln_target] = ActiveSupport::OrderedHash.new() end @sln_platforms[sln_platform][sln_target][project_guid] = ProjectConfig.new(project_guid,proj_platform,proj_target) end end end #module ProjBuild end #module Pipeline