require 'xml' require 'pipeline/coding/projbuild/project' require 'pipeline/coding/projbuild/generators/vsshared' module Pipeline module ProjBuild class VS2008VSSlnExporter < VSProjShared @@log = nil def VS2008VSSlnExporter.log @@log = Log.new( 'vs2008vsslnexporter' ) if @@log == nil @@log end def initialize() @log = VS2008VSSlnExporter.log config = Pipeline::Config.instance # @p4 = SCM::Perforce.new() # @p4.port = config.sc_server # @p4.client = config.sc_workspace # @p4.user = config.sc_username # @p4.connect() end def export(path, project) path = VS2008Generator.get_solution_filename(path) #add_in_p4(path) as of yet adding is not supported - but I think it should be eventually. sln_guid = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}" proj_build = ProjBuildData::instance() File.open(path,"w+") { |file| main_filename = VS2008Generator.get_project_filename(project.name) #identifying character sequence for a 2008 sln file.putc(0xef) file.putc(0xbb) file.putc(0xbf) file.write("\n") file.write("Microsoft Visual Studio Solution File, Format Version 10.00\n") file.write("# Visual Studio 2008\n") file.write("Project(\"#{sln_guid}\") = \"#{project.name}\", \"#{main_filename}\", \"#{project.guid}\"\n") file.write("\tProjectSection(ProjectDependencies) = postProject\n") dep_guids = ActiveSupport::OrderedHash.new() project.dep_guids.each { |dep_guid| dep_guids[dep_guid] = true dep_project = proj_build.dependent_projects[dep_guid] file.write("\t\t#{dep_project.guid} = #{dep_project.guid}\n") } file.write("\tEndProjectSection\n") file.write("EndProject\n") proj_build.dependent_projects.keys.each { |dep_guid| next if dep_guid == project.guid dep_project = proj_build.dependent_projects[dep_guid] filename = OS::Path.combine(dep_project.path,dep_project.name) filename = OS::Path.dos_format(VS2008Generator.get_project_filename(filename)) file.write("Project(\"#{sln_guid}\") = \"#{dep_project.name}\", \"#{filename}\", \"#{dep_project.guid}\"\n") file.write("EndProject\n") } file.write("Global\n") file.write("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n") proj_build.sln_platforms.each { |sln_platform,targets| targets.each { |sln_target,proj_cfg| platform_out = VSProjShared.get_platform_string(sln_platform) config = "#{sln_target}|#{platform_out}" file.write("\t\t#{config} = #{config}\n") } } file.write("\tEndGlobalSection\n") file.write("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n") proj_build.sln_platforms.each { |sln_platform,targets| targets.each { |sln_target,proj_cfgs| #First process the start up project. proj_cfg = proj_cfgs[project.guid] prj_cfg = VSUtility.create_config_name(proj_cfg.platform,proj_cfg.target) sln_cfg = "#{sln_target}|#{VSProjShared.get_platform_string(sln_platform)}" config_out = "#{project.guid}.#{sln_cfg}" file.write("\t\t#{config_out}.ActiveCfg = #{prj_cfg}\n") file.write("\t\t#{config_out}.Build.0 = #{prj_cfg}\n") #Process its dependent projects. proj_build.dependent_projects.keys.each { |guid| next if proj_cfgs[guid] == nil proj_cfg = proj_cfgs[guid] prj_cfg = VSUtility.create_config_name(proj_cfg.platform,proj_cfg.target) sln_cfg = "#{sln_target}|#{VSProjShared.get_platform_string(sln_platform)}" config_out = "#{guid}.#{sln_cfg}" file.write("\t\t#{config_out}.ActiveCfg = #{prj_cfg}\n") if dep_guids[guid] or guid == project.guid then file.write("\t\t#{config_out}.Build.0 = #{prj_cfg}\n") end } } } file.write("\tEndGlobalSection\n") file.write("\tGlobalSection(SolutionProperties) = preSolution\n") file.write("\t\tHideSolutionNode = FALSE\n") file.write("\tEndGlobalSection\n") file.write("EndGlobal\n") } revert_unchanged(path) true end end class VS2008VSProjExporter < VSProjExporterShared @@log = nil def VS2008VSProjExporter.log @@log = Log.new( 'vs2008vsprojexporter' ) if @@log == nil @@log end def initialize() @log = VS2008VSProjExporter.log @version = "2008" end protected def create_project_node( project ) xml_node = XML::Node.new("VisualStudioProject") xml_node.attributes["ProjectType"] = "Visual C++" xml_node.attributes["Version"] = "9.00" xml_node.attributes["Name"] = project.name xml_node.attributes["ProjectGUID"] = project.guid xml_node.attributes["Keyword"] = "Win32Proj" xml_node.attributes["TargetFrameworkVersion"] = "131072" xml_node end end class VS2008VSProjLoader < VSProjShared @@log = nil def VS2008VSProjLoader.log @@log = Log.new( 'vs2008vsprojloader' ) if @@log == nil @@log end def initialize() @log = VS2008VSProjLoader.log end def import( path, project_path = nil ) if File.exists?(path) == false then @log.debug("couldn't open #{path}") return false end @log.debug("checking #{path} with vs2008 project loader") data = nil parser = XML::Parser.file(path) doc = parser.parse if doc.root.attributes["Version"] != "9.00" then @log.debug("#{path} not in the correct format") return false end strip_unused_info(doc) @log.debug("parsing #{path} with vs2008 project loader") @curr_project = Info::Project.new(OS::Path.get_basename(path)) @curr_project.name = doc.root["Name"] @curr_project_path = project_path if project_path != nil @curr_project_path = OS::Path.normalise(OS::Path.get_directory(path)) unless project_path != nil #files files_node = doc.find_first("Files") import_proj_files(files_node,@curr_project) #root config configs_node = doc.find_first("Configurations") import_root_configs(configs_node) @curr_project.guid = doc.root.attributes["ProjectGUID"] @curr_project.config.xml_data = XML::Node.new("config") @log.debug("parsed #{path}") return true, @curr_project end protected def strip_unused_info( doc ) doc.find("/VisualStudioProject/Configurations/Configuration/Tool").each { |xml_node| case xml_node.attributes["Name"] when "VCWebDeploymentTool": xml_node.remove! end } end def import_file_config_hierarchy( xml_in ) xml_out = nil if xml_in.name == "FileConfiguration" then xml_out = XML::Node.new("config") clone_attributes(xml_in,xml_out) elsif xml_in.name == "Tool" then xml_out = XML::Node.new(xml_in.attributes["Name"]) clone_attributes(xml_in,xml_out) elsif xml_in.name == "custombuild" then xml_out = XML::Node.new(xml_in.attributes["Name"]) clone_attributes(xml_in,xml_out) end # if xml_out == nil then # # ProjBuilder.log.debug("Not loaded!") # ProjBuilder.log.debug(xml_in) # end if xml_out != nil and xml_in.children? then xml_in.children.each { |child| xml_new = import_file_config_hierarchy(child) xml_out << xml_new if xml_new != nil } end xml_out end def import_proj_files( xml_node, filter_node ) xml_node.find("File").each do |xml_file_node| file_path = xml_file_node.attributes["RelativePath"] file_path = File.expand_path(file_path,@curr_project_path) new_file = Info::File.new(file_path) xml_file_node.find("FileConfiguration").each do |xml_file_config_node| platform, target = VSProjShared.parse_config_name(xml_file_config_node.attributes["Name"]) next if (platform == nil or target == nil) config = new_file.platforms[platform].get_target(target) config.xml_data = import_file_config_hierarchy(xml_file_config_node) end filter_node.files << new_file end xml_node.find("Filter").each do |xml_filter_node| new_filter_node = Info::Filter.new(xml_filter_node.attributes["Name"]) filter_node.filters << new_filter_node import_proj_files(xml_filter_node,new_filter_node) end end def import_config_hierarchy( xml_in, doc, platform, target ) xml_out = nil if xml_in.name == "Configuration" then doc.root = XML::Node.new("config") xml_out = XML::Node.new("vc") doc.root << xml_out case xml_in["ConfigurationType"] when "1": doc.root["type"] = :exe.to_s when "2": doc.root["type"] = :dll.to_s when "4": doc.root["type"] = :library.to_s end doc.root["output_path"] = xml_in["OutputDirectory"].downcase if xml_in["OutputDirectory"] elsif xml_in.name == "Tool" or xml_in.name == "custombuild" found = false if xml_in["Name"] == "VCCLCompilerTool" or xml_in["Name"] == "VCCLX360CompilerTool" then if xml_in["AdditionalIncludeDirectories"] then found = true xml_incpaths = create_xml_path(doc.root,"includepaths") dirs = xml_in["AdditionalIncludeDirectories"].split(";") xml_in.attributes.get_attribute("AdditionalIncludeDirectories").remove! dirs.each { |dir| xml_incpath = XML::Node.new("includepath") xml_incpath["path"] = dir xml_incpaths << xml_incpath } end if xml_in["PreprocessorDefinitions"] then found = true xml_defines = create_xml_path(doc.root,"defines") defines = xml_in["PreprocessorDefinitions"].split(";") xml_in.attributes.get_attribute("PreprocessorDefinitions").remove! defines.each { |define| next if define == "" xml_define = XML::Node.new("define") xml_define["value"] = define xml_defines << xml_define } end if xml_in["ForcedIncludeFiles"] then found = true xml_forceincludes = create_xml_path(doc.root,"forceincludes") forceincludes = xml_in["ForcedIncludeFiles"].split(";") xml_in.attributes.get_attribute("ForcedIncludeFiles").remove! i = 0 forceincludes.each { |forceinclude| xml_forceinclude = XML::Node.new("forceinclude") xml_forceinclude["path"] = forceinclude xml_forceinclude["order"] = i.to_s i = i + 1 xml_forceincludes << xml_forceinclude } end end if (xml_in.attributes["Name"] != nil and xml_in.attributes.length > 1) then xml_out = XML::Node.new(xml_in.attributes["Name"]) clone_attributes(xml_in,xml_out) end elsif xml_in.name == "DebuggerTool" then xml_out = XML::Node.new("DebuggerTool") clone_attributes(xml_in,xml_out) end # if xml_out == nil then # # ProjBuilder.log.debug("Not loaded!") # ProjBuilder.log.debug(xml_in) # end if xml_out != nil and xml_in.children? then xml_in.children.each { |child| xml_new = import_config_hierarchy(child,doc,platform,target) xml_out << xml_new if xml_new != nil } end xml_out end def import_root_config( xml_node ) platform, target = VSProjShared.parse_config_name(xml_node.attributes["Name"]) return nil if ( platform.nil? or target.nil? ) config = @curr_project.platforms[platform].get_target(target) doc = XML::Document.new() import_config_hierarchy(xml_node,doc,platform,target) config.xml_data = doc.root end def import_root_configs( xml_node ) config_nodes = xml_node.find("Configuration") config_nodes.each do |xml_config_node| import_root_config(xml_config_node) end end end class VS2008SlnLoader attr_reader :projects @@log = nil def VS2008SlnLoader.log @@log = Log.new( 'vs2008slnloader' ) if @@log == nil @@log end def initialize() @log = VS2008SlnLoader.log end def import( path ) @log.debug("parsing #{path}") #make sure the path is absolute... #set the parent path @sln_dir = OS::Path.normalise(OS::Path.get_directory(path)) sln_lines = File.readlines(path) # puts(sln_lines[0]) #first line is just a file format comment state = :none line_buffer = Array.new() sln_lines.each { |line| case state when :none if line.index("Project") == 0 then state = :project line_buffer = Array.new() line_buffer << line end if line.index("Global") == 0 then state = :global line_buffer = Array.new() line_buffer << line end when :project if line.index("EndProject") == 0 then state = :none line_buffer << line load_sln_project_section(line_buffer) else line_buffer << line end when :global if line.index("EndGlobal") == 0 then state = :none line_buffer << line load_sln_global_section(line_buffer) else line_buffer << line end end } return true, nil end def load_sln_project_section( lines ) tokens = lines[0].split("=") tokens = tokens[1].split(",") name = tokens[0] proj_file = tokens[1].strip proj_file = OS::Path.normalise(proj_file[1...(proj_file.size - 1)]) proj_file = File.expand_path(proj_file,@sln_dir) guid = tokens[2] deps = Array.new() state = :none lines.each { |line| case state when :none if line.strip.index("ProjectSection(ProjectDependencies) = postProject") == 0 then state = :deps end when :deps if line.strip.index("EndProjectSection") == 0 then state = :none else deps << line.split("=")[0].strip end end } loader = VS2008VSProjLoader.new() ret, project = loader.import(proj_file) ProjBuildData::instance().projects[project.guid] = project if ret == true end def load_sln_global_section( lines ) state = :none lines.each { |line| case state when :none if line.strip.index("GlobalSection(ProjectConfigurationPlatforms) = postSolution") == 0 then state = :configs end when :configs if line.strip.index("EndGlobalSection") == 0 then state = :none else tokens = line.strip.split("=") prj_data = tokens[0] sln_cfg = tokens[1].strip tokens = prj_data.split(".") guid = tokens[0].strip prj_cfg = tokens[1].strip command = tokens[2].strip if command == "ActiveCfg" then sln_tokens = sln_cfg.split("|") sln_platform = sln_tokens[1] sln_target = sln_tokens[0] proj_platform, proj_target = VSProjShared.parse_config_name(prj_cfg) ProjBuildData::instance().add_projectconfig(sln_platform,sln_target,guid,proj_platform,proj_target) end end end } end end class VS2008Generator @@log = nil def VS2008Generator.log @@log = Log.new( 'VS2008Generator' ) if @@log == nil @@log end def initialize() @log = VS2008Generator.log end GENERATOR_ID = "2008" @@unity_build_filenames = nil # if true filenames that are created have "_unity" in them. def set_unity_build_filenames( set ) @@unity_build_filenames = set end def get_unity_build_filenames( ) @@unity_build_filenames end def self.get_project_filename( basename ) (@@unity_build_filenames and not basename.downcase.include?("shaders")) ? "#{basename}_#{GENERATOR_ID}_unity.vcproj" : "#{basename}_#{GENERATOR_ID}.vcproj" end def self.get_solution_filename( basename ) (@@unity_build_filenames and not basename.downcase.include?("shaders")) ? "#{basename}_#{GENERATOR_ID}_unity.sln" : "#{basename}_#{GENERATOR_ID}.sln" end def export( path, project, options = nil ) VS2008Generator.log.debug("exporting #{path} through vs2008 path") if options != nil and options[:type] == :solution then return VS2008VSSlnExporter.new().export(path,project) else path = VS2008Generator.get_project_filename(path) return VS2008VSProjExporter.new().export(path,project) end false end def import( path, options = nil ) case OS::Path::get_extension(path).downcase when 'sln': @log.debug("import solution #{path}") loader = VS2008SlnLoader.new() return loader.import(path) when 'vcproj': @log.debug("import project #{path}") loader = VS2008VSProjLoader.new() return loader.import(path) end false end end end #module ProjBuild end #module Pipeline