# # File:: projbuild.rb # # Pipeline::Cross platform generic project file builder system that supports # output to multiple target compilers. # # Author:: Greg Smith # Date:: 15 January 2010 # # #----------------------------------------------------------------------------- # Uses #----------------------------------------------------------------------------- require "pipeline/coding/projbuild/generators/csharp" require "pipeline/coding/projbuild/generators/ragegen" require "pipeline/coding/projbuild/generators/rageprojbuilder" require "pipeline/coding/projbuild/generators/vs2005" require "pipeline/coding/projbuild/generators/vs2008" require "pipeline/coding/projbuild/generators/vs2010" require "pipeline/coding/projbuild/generators/shadermakefile" require "pipeline/coding/projbuild/generators/qaitems" require "pipeline/coding/projbuild/generators/internal" require "pipeline/coding/projbuild/unity_build" require "pipeline/os/path" require "pipeline/gui/exception_dialog" require "pipeline/log/log" #----------------------------------------------------------------------------- # Implementation #----------------------------------------------------------------------------- module Pipeline module ProjBuild class ProjBuilder attr_accessor :unity_build attr_accessor :vs2010 @@log = nil def ProjBuilder.log @@log = Log.new( 'projbuild' ) if @@log == nil @@log end def initialize( vs2010 = false ) @vs2010 = vs2010 # sadly this code needs to specialise for VS2010, IIRC for PSCCustom jobs, under VS2010 the interal macros differ. OS::Path.set_downcase_on_normalise(false) @generators = Hash.new() @generators[:csharp] = CSharpGenerator.new() @generators[:vs2005] = VS2005Generator.new() @generators[:vs2008] = VS2008Generator.new() @generators[:vs2010] = VS2010Generator.new() @generators[:internal] = ProjBuildGenerator.new() @generators[:ragegen] = RageGenGenerator.new() @generators[:rageprojbuilder] = RageProjBuilder.new(@vs2010) @generators[:shadermakefile] = ShaderMakefileGenerator.new() @generators[:qaitems] = QAItemsGenerator.new() @config = Pipeline::Config.instance @config.project.load_config @p4 = SCM::Perforce.new() @p4.port = @config.sc_server @p4.client = @config.sc_workspace @p4.user = @config.sc_username @p4.connect() @projects = Array.new end #possible extra options: #single file, or recursively #look for related files (pass _2008, ask it to look for _2008 variants etc) def import( path, forcetype = nil, options = nil ) begin initialize(@vs2010) # DW - what on earth is this all about? # DW : sigh it needs set here due to design flaws with the project builder - since at import we need to know if a unity build is enabled due to 2010 vcxproj project references. if (options[:unity_build_enabled]) @generators.values.each { |g| g.set_unity_build_filenames( true ) } else @generators.values.each { |g| g.set_unity_build_filenames( false ) } end #ProjBuildData::instance().reset() GC.enable() GC.start() GC.disable() path = OS::Path.normalise(path) #ProjBuilder::log.info("Importing #{path}...") #ProjBuilder::log.info("Forcetype: #{forcetype.to_s}") if forcetype != nil #Does the file exist? #raise Exception.new("#{path} doesn't exist!") if File.exists?(path) == false if forcetype == nil then #pass it to each generator @generators.values.each { |g| ret, @projects = g.import(path, options) ProjBuilder::log.info("..generator import returned false") if ret == false return false if ret == false if @projects != nil then @projects.each do |project| ProjBuildData::instance().projects[project.guid] = project end end return true } else #ProjBuilder::log.info("..generator import path #{path} options #{options}") ret, @projects = @generators[forcetype].import(path, options) ProjBuilder::log.info("@projects.length is #{@projects.length}") if @projects and @projects.length <= 0 return false if ret == false ProjBuilder::log.info("..generator import returned false") if ret == false if @projects != nil then @projects.each do |project| ProjBuildData::instance().projects[project.guid] = project end end return true end return false rescue Exception => ex puts "Unhandled exception: #{ex.message}" puts ex.backtrace.join( "\n" ) #log.error( ex, 'Unhandled exception in projbuild' ) GUI::ExceptionDialog::show_dialog( ex, 'Unhandled exception in projbuild' ) ensure #LogSystem::instance( ).shutdown( ) end end def export( path, forcetype = nil, options = nil ) begin retval = false GC.enable() path = OS::Path.normalise(path) #ProjBuilder::log.info("exporting(projbuild.rb export) #{path}") ProjBuilder::log.debug("forcetype #{forcetype.to_s}") if forcetype != nil proj_build = ProjBuildData::instance() #ProjBuilder::log.info("No projects to export - did the import succeed?") if @projects == nil return true if @projects == nil if @projects.size < 1 ProjBuilder::log.info("Generator did not create any projects to export ( projbuild.rb)!\n") return false end ProjBuilder::log.info("About to export #{@projects.size} projects") if @projects.size > 1 @projects.each do |project| if project == nil #HACK: In some generators, such as the RageGen solution generator, we can not rely on the export #call of the generator to do our work. For now, gracefully handle the instances were project #is nil so we do not crash. ProjBuildData::instance().reset() return true end #The output path will be the directory of the 'path' object, plus the name of the project. path = OS::Path.combine(File.dirname(path), project.name) #TODO: Determine if we can use the project.path variable... unity_setup = "%RS_TOOLSROOT%/etc/project.xml/project/branches/branch/codeconfigs/codeconfig/unity_build/@enabled" if (not options[:unity_build_enabled].nil?) and options[:unity_build_enabled] #path = "#{path}_unity" ProjBuilder::log.info("Unity build enabled via #{unity_setup} for #{path}") unity_config_file = "#{project.name}.unity" ret = unity_process( unity_config_file, project ) if ret == false or ret.nil? ProjBuilder::log.warn("\n\nWARNING : *** Unity build is enabled but a unity build failed to run ok - possibly no unity file - this project will not be built ***\n\n") return false end else ProjBuilder::log.info("Unity build disabled via #{unity_setup}.") end # filenames will be built with _unity in them if unity build is enabled via options, # but it may not have built a unity configured project file as it can still be disabled # via a missing .unity file, or disabled in the .unity file, hence the name 'unity_build_filenames' if (options[:unity_build_enabled]) #ProjBuilder::log.info("Setting all generators (#{@generators.length}) to have unity filenames enabled.") @generators.values.each { |g| g.set_unity_build_filenames( true ) } else #ProjBuilder::log.info("Setting all generators (#{@generators.length}) to have unity filenames DISABLED.") @generators.values.each { |g| g.set_unity_build_filenames( false ) } end do_checkout = false # Do not test for the file's attributes as writable. # All .sln and .vcproj files are marked writable on client. if forcetype == nil then ProjBuilder::log.info("Forcetype is not set - whatever that achieves?") #pass it to each generator @generators.values.each { |g| if g.export(path, project, options) then retval = true break end } else #ProjBuilder::log.info("Forcetype is set #{forcetype} - whatever that achieves?") #ProjBuilder::log.info("class #{@generators[forcetype].class}") retval = @generators[forcetype].export(path, project, options) end if do_checkout then @p4.run_revert("-a",path) end if retval == false then ProjBuilder::log.debug("Did not export #{path}!") break; end end ProjBuildData::instance().reset() return retval rescue Exception => ex puts "Unhandled exception: #{ex.message}" puts ex.backtrace.join( "\n" ) ProjBuilder::log.error( ex, 'Unhandled exception in projbuild' ) GUI::ExceptionDialog::show_dialog( ex, 'Unhandled exception in projbuild' ) ensure LogSystem::instance( ).shutdown( ) end end end end end