Files
2025-09-29 00:52:08 +02:00

775 lines
15 KiB
Ruby
Executable File

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