775 lines
15 KiB
Ruby
Executable File
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
|