# Copyright (C) 2013 Jaedyn K. Draper # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import stat import xml.etree.ElementTree as ET import xml.dom.minidom as minidom import os import sys from . import project_generator from . import _shared_globals from . import projectSettings from . import log import csbuild class project_generator_qtcreator( project_generator.project_generator ): """ Generator used to generate QtCreator solutions. Generator options: --qtpath: Path to QtCreator configuration (default ~/.config, should contain QtProject directory) """ def __init__( self, path, solutionname, extraargs ): project_generator.project_generator.__init__( self, path, solutionname, extraargs ) self.qtpath = os.path.expanduser( csbuild.GetOption( "qtpath" ) ) self.update_qtcreator_config( ) @staticmethod def AdditionalArgs( parser ): parser.add_argument( "--qtpath", help = "Path to QtCreator configuration (default ~/.config, should contain QtProject directory)", default = "~/.config" ) def _writeProject( self, projectDict, parentNames ): depth = len( parentNames ) if depth == 0: parentPath = "" elif depth == 1: parentPath = parentNames[0] else: parentPath = os.path.join( *parentNames ) # Grab a random project from the dictionary, first one will do. # These values don't matter much as they're likely to be the same (or close enough to the same for our purposes) # across all targets. archDict = projectDict[list(projectDict.keys())[0]] toolchainDict = archDict[list(archDict.keys())[0]] project = toolchainDict[list(toolchainDict.keys())[0]] projectpath = os.path.join( self.rootpath, parentPath, project.name ) if not os.access(projectpath , os.F_OK): os.makedirs( projectpath ) log.LOG_INFO( "Creating project {}.pro".format( projectpath ) ) launcher = "{}_launcher".format( project.name ) launcherpath = os.path.join( projectpath, launcher ) with open( os.path.join( projectpath, "{}.pro".format( project.name ) ), "w" ) as f: if project.allsources: f.write( "SOURCES += \\\n" ) for source in project.allsources: f.write( "\t{} \\\n".format( os.path.relpath( source, projectpath ) ) ) if project.allheaders: f.write( "\nHEADERS += \\\n" ) for header in project.allheaders: f.write( "\t{} \\\n".format( os.path.relpath( header, projectpath ) ) ) f.write( "\nDESTDIR = {}\n\n".format( project.outputDir ) ) f.write( "TARGET = {}\n\n".format( project.outputName ) ) if project.type == csbuild.ProjectType.Application: f.write( "TEMPLATE = app\n\n" ) else: f.write( "TEMPLATE = lib\n\n" ) f.write( "INCLUDEPATH += \\\n" ) for incdir in project.includeDirs: f.write( "\t{} \\\n".format( incdir ) ) if project.cxxCompilerFlags: f.write( "\nQMAKE_CXXFLAGS += {}\n".format( " ".join( project.cxxCompilerFlags ) ) ) try: if project.activeToolchain.Compiler().cppStandard: f.write( "\nQMAKE_CXXFLAGS += -std={}\n".format( project.activeToolchain.Compiler().cppStandard ) ) if "11" in project.activeToolchain.Compiler().cppStandard: f.write( "\nCONFIG += c++11") elif "14" in project.activeToolchain.Compiler().cppStandard: f.write( "\nCONFIG += c++14") except: pass if project.ccCompilerFlags: f.write( "\nQMAKE_CFLAGS += {}\n".format( " ".join( project.ccCompilerFlags ) ) ) try: if project.activeToolchain.Compiler().cStandard: f.write( "\nQMAKE_CFLAGS += -std={}\n".format( project.activeToolchain.Compiler().cStandard ) ) except: pass #with open( launcherpath, "w" ) as f: # f.write("#!/bin/bash\n\n") # written = False # for project in projectDict.values(): # if written: # f.write('el') # f.write('if [ "$CSB_BUILD_TARGET" = "{}" ]; then\n'.format(project.targetName)) # executable = os.path.join(project.outputDir, project.outputName) # f.write('\techo "Executing {} $@ ({} target)..."\n'.format(project.targetName, executable)) # f.write('\tcd {}\n'.format(project.outputDir)) # f.write('\t./{} $@\n'.format(project.outputName)) # f.write('\texit $?\n') # written = True # f.write('elif [ "$CSB_BUILD_TARGET"=="ALL TARGETS" ]; then\n') # f.write('\techo "No target selected. Please select a target to execute."\n') # f.write('\texit 1\n') # f.write('else\n') # f.write('\techo "No executable defined for project {} / target $CSB_BUILD_TARGET."\n'.format(project.name)) # f.write('\texit 1\n') # f.write('fi') #os.chmod( # launcherpath, # stat.S_IXUSR | # stat.S_IRUSR | # stat.S_IWUSR | # stat.S_IXGRP | # stat.S_IRGRP | # stat.S_IWGRP | # stat.S_IROTH | # stat.S_IXOTH #) with open( os.path.join(projectpath, "Makefile"), "w" ) as f: projstr = " --project {}".format( project.name ) make_dir = os.getcwd( ) f.write( "all:\n\t@cd {} && {} ./{}{} ${{ARGS}}\n\n".format( make_dir, sys.executable, csbuild.mainFile, projstr ) ) f.write( "clean:\n\t@cd {} && {} ./{}{} ${{ARGS}} --clean\n\n".format( make_dir, sys.executable, csbuild.mainFile, projstr ) ) f.write( "install:\n\t@cd {} && {} ./{}{} ${{ARGS}} --install\n\n".format( make_dir, sys.executable, csbuild.mainFile, projstr ) ) def _writeSubdirsProject( self, projectGroup, parentNames, projlist ): depth = len( parentNames ) if depth == 0: parentPath = "" elif depth == 1: parentPath = parentNames[0] else: parentPath = os.path.join( *parentNames ) parentNames.append( projectGroup.name ) solutionname = projectGroup.name if not solutionname: solutionname = self.solutionname grouppath = os.path.join( self.rootpath, parentPath, projectGroup.name ) if not os.access(grouppath , os.F_OK): os.makedirs( grouppath ) allsubprojects = set( ) solutionpath = os.path.join( grouppath, "{}.pro".format( solutionname ) ) log.LOG_INFO( "Creating subdirs project {}".format( solutionpath ) ) with open( solutionpath, "w" ) as f: f.write( "TEMPLATE = subdirs\n\n" ) f.write( "SUBDIRS += \\\n" ) for group in projectGroup.subgroups.items( ): f.write( "\t{} \\\n".format( group[0] ) ) subprojects = set( ) self._writeSubdirsProject( group[1], parentNames, subprojects ) allsubprojects |= subprojects for proj in projectGroup.projects.items( ): f.write( "\t{} \\\n".format( proj[0] ) ) self._writeProject( proj[1], parentNames ) allsubprojects.add( proj[0] ) projlist |= allsubprojects projstr = "" if depth != 0: for proj in allsubprojects: projstr += " --project {}".format( proj ) with open( os.path.join(grouppath, "Makefile"), "w" ) as f: make_dir = os.getcwd( ) f.write( "all:\n\t@cd {} && {} ./{}{} ${{ARGS}}\n\n".format( make_dir, sys.executable, csbuild.mainFile, projstr ) ) f.write( "clean:\n\t@cd {} && {} ./{}{} ${{ARGS}} --clean\n\n".format( make_dir, sys.executable, csbuild.mainFile, projstr ) ) f.write( "install:\n\t@cd {} && {} ./{}{} ${{ARGS}} --install\n\n".format( make_dir, sys.executable, csbuild.mainFile, projstr ) ) del parentNames[-1] def _writeSharedFile( self ): add = ET.SubElement root = ET.Element( "qtcreator" ) data = add( root, "data" ) add( data, "variable" ).text = "ProjectExplorer.Project.ActiveTarget" add( data, "value", type = "int" ).text = "0" data = add( root, "data" ) add( data, "variable" ).text = "ProjectExplorer.Project.Target.0" vm = add( data, "valuemap", type = "QVariantMap" ) add( vm, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName" ).text = "CSBuild-AutoGenerated" add( vm, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DisplayName" ).text = "CSBuild-AutoGenerated" add( vm, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.Id" ).text = "{csbuild-default-profile}" add( vm, "value", type = "int", key = "ProjectExplorer.Target.ActiveBuildConfiguration" ).text = "0" add( vm, "value", type = "int", key = "ProjectExplorer.Target.ActiveDeployConfiguration" ).text = "0" add( vm, "value", type = "int", key = "ProjectExplorer.Target.ActiveRunConfiguration" ).text = "0" stepcount = 0 for targetName in list(_shared_globals.alltargets) + ["ALL_TARGETS"]: for architecture in list(_shared_globals.allarchitectures) + ["ALL_ARCHITECTURES"]: target = "{}-{}".format(targetName, architecture) vm2 = add( vm, "valuemap", type = "QVariantMap", key = "ProjectExplorer.Target.BuildConfiguration.{}".format( stepcount ) ) stepcount += 1 add( vm2, "value", type = "QString", key = "ProjectExplorer.BuildConfiguration.BuildDirectory" ).text = self.rootpath def addTarget( clean ): vm3 = add( vm2, "valuemap", type = "QVariantMap", key = "ProjectExplorer.BuildConfiguration.BuildStepList.{}".format( "1" if clean else "0" ) ) vm4 = add( vm3, "valuemap", type = "QVariantMap", key = "ProjectExplorer.BuildStepList.Step.0" ) add( vm4, "value", type = "bool", key = "ProjectExplorer.BuildStep.Enabled" ).text = "true" add( vm4, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName" ).text = "Make" add( vm4, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DisplayName" ) add( vm4, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.Id" ).text = "Qt4ProjectManager.MakeStep" vl = add( vm4, "valuelist", type = "QVariantList", key = "Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments" ) add( vl, "value", type = "QString" ).text = "-w" add( vl, "value", type = "QString" ).text = "-r" add( vm4, "value", type = "bool", key = "Qt4ProjectManager.MakeStep.Clean" ).text = "true" if clean else "false" add( vm4, "value", type = "QString", key = "Qt4ProjectManager.MakeStep.MakeArguments" ).text = "{}ARGS='{}{} {}'".format( "clean " if clean else "", "{} ".format(self.extraargs) if self.extraargs else "", "-t {}".format(targetName) if targetName != "ALL_TARGETS" else "--all-targets", "--arch {}".format(architecture) if architecture != "ALL_ARCHITECTURES" else "--all-architectures") add( vm4, "value", type = "QString", key = "Qt4ProjectManager.MakeStep.MakeCommand" ) add( vm3, "value", type = "int", key = "ProjectExplorer.BuildStepList.StepsCount" ).text = "1" add( vm3, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName" ).text = "Clean" if clean else "Build" add( vm3, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DisplayName" ) add( vm3, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.Id" ).text = "ProjectExplorer.BuildSteps.{}".format( "Clean" if clean else "Build" ) addTarget( False ) addTarget( True ) add( vm2, "value", type = "int", key = "ProjectExplorer.BuildConfiguration.BuildStepListCount" ).text = "2" add( vm2, "value", type = "bool", key = "ProjectExplorer.BuildConfiguration.ClearSystemEnvironment" ).text = "false" #ev = add( vm2, "valuelist", type = "QVariantList", # key = "ProjectExplorer.BuildConfiguration.UserEnvironmentChanges" ) #add( ev, "value", type = "QString" ).text = "CSB_BUILD_TARGET={}".format( targetName ) add( vm2, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName" ).text = target add( vm2, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.DisplayName" ).text = target add( vm2, "value", type = "QString", key = "ProjectExplorer.ProjectConfiguration.Id" ).text = "Qt4ProjectManager.Qt4BuildConfiguration" add( vm2, "value", type = "QString", key = "Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration" ).text = "2" add( vm2, "value", type = "bool", key = "Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild" ).text = "true" add( vm, "value", type = "int", key = "ProjectExplorer.Target.BuildConfigurationCount" ).text = str( stepcount ) data = add( root, "data" ) add( data, "variable" ).text = "ProjectExplorer.Project.TargetCount" add( data, "value", type = "int" ).text = "1" data = add( root, "data" ) add( data, "variable" ).text = "ProjectExplorer.Project.Updater.FileVersion" add( data, "value", type = "int" ).text = "15" outxml = ET.tostring( root ) self._printXml( outxml, os.path.join( self.rootpath, "{}.pro.shared".format( self.solutionname ) ), "QtCreatorProject" ) def WriteProjectFiles( self ): log.LOG_BUILD( "Writing QtCreator solution {}".format( self.solutionname ) ) parentNames = [] projlist = set( ) self._writeSubdirsProject( projectSettings.rootGroup, parentNames, projlist ) self._writeSharedFile( ) def update_qtcreator_config( self, ): profilespath = os.path.join( self.qtpath, "QtProject", "qtcreator", "profiles.xml" ) tree = ET.parse( profilespath ) root = tree.getroot( ) profilenum = 0 needsadd = True metaindex = 0 randomDebuggerInformation = "" randomToolchain = "" randomQtInformation = "" randomProfileIcon = "" index = 0 for data in root.findall( "data" ): variable = data.find( "variable" ) if variable.text == "Profile.Count": value = data.find( "value" ) profilenum = int( value.text ) + 1 value.text = str( profilenum ) metaindex = index else: valuemap = data.find( "valuemap" ) if valuemap is not None: for value in valuemap.findall( "value" ): if value.get( "key" ) == "PE.Profile.Id": if value.text == "{csbuild-default-profile}": needsadd = False elif value.get( "key" ) == "PE.Profile.Icon": randomProfileIcon = value.text vm2 = valuemap.find( "valuemap" ) for value in vm2.findall( "value" ): if value.get( "key" ) == "Debugger.Information": randomDebuggerInformation = value.text elif value.get( "key" ) == "PE.Profile.ToolChain": randomToolchain = value.text elif value.get( "key" ) == "QtSupport.QtInformation": randomQtInformation = value.text index += 1 if not needsadd: return log.LOG_INFO( "Creating csbuild kit entry in {}".format( profilespath ) ) data = ET.Element( "data" ) ET.SubElement( data, "variable" ).text = "Profile.{}".format( profilenum - 1 ) valuemap = ET.SubElement( data, "valuemap", type = "QVariantMap" ) ET.SubElement( valuemap, "value", type = "bool", key = "PE.Profile.AutoDetected" ).text = "false" vm2 = ET.SubElement( valuemap, "valuemap", type = "QVariantMap", key = "PE.Profile.Data" ) ET.SubElement( vm2, "value", type = "QString", key = "Android.GdbServer.Information" ) ET.SubElement( vm2, "value", type = "QString", key = "Debugger.Information" ).text = randomDebuggerInformation ET.SubElement( vm2, "value", type = "QString", key = "PE.Profile.Device" ).text = "Desktop Device" ET.SubElement( vm2, "value", type = "QByteArray", key = "PE.Profile.DeviceType" ).text = "Desktop" ET.SubElement( vm2, "value", type = "QString", key = "PE.Profile.SysRoot" ) ET.SubElement( vm2, "value", type = "QString", key = "PE.Profile.ToolChain" ).text = randomToolchain ET.SubElement( vm2, "value", type = "QString", key = "QtPM4.mkSpecInformation" ) ET.SubElement( vm2, "value", type = "int", key = "QtSupport.QtInformation" ).text = randomQtInformation ET.SubElement( valuemap, "value", type = "QString", key = "PE.Profile.Icon" ).text = randomProfileIcon ET.SubElement( valuemap, "value", type = "QString", key = "PE.Profile.Id" ).text = "{csbuild-default-profile}" ET.SubElement( valuemap, "valuelist", type = "QVariantList", key = "PE.Profile.MutableInfo" ) ET.SubElement( valuemap, "value", type = "QString", key = "PE.Profile.Name" ).text = "CSBuild-AutoGenerated" ET.SubElement( valuemap, "value", type = "bool", key = "PE.Profile.SDK" ) root.insert( metaindex, data ) outxml = ET.tostring( root ) self._printXml( outxml, profilespath, "QtCreatorProfiles" ) def _printXml( self, inxml, outfile, doctype ): if sys.version_info >= (3, 0): inxml = inxml.decode("utf-8") outxml = '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE {}>\n<!-- Written by CSBuild using input from QTCreator -->\n{}'.format( doctype, inxml ) formatted = minidom.parseString( outxml ).toprettyxml( " ", "\n" ) inlist = formatted.split( "\n" ) outlist = [] for line in inlist: if line.strip( ): outlist.append( line ) final = "\n".join( outlist ) with open( outfile, "w" ) as f: f.write( final )
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 15641 | ShadauxCat |
-Added support for prebuilt projects using @csbuild.prebuilt - these fit into the project hierarchy and take all the normal things a project can take, but are considered to always be Up-To-Date and are never built. These are used for referencing prebuilt third-party libraries. -Added support for shell projects using @csbuild.shellProject - these are a small variation on prebuilt in that they do not build, but they DO show up in generated IDE solutions, useful for things like projects that only contain headers or data files and never build, but need to be available for editing. -Improved csbuild's cleanup process - it now keeps track of its running processes and if it receives SIGTERM or SIGINT, it will immediately kill all of those running processes, remove any files they were in the process of writing (to ensure no corrupt data is left on disk), and terminate the GUI. It also now exits with os._exit() rather than sys.exit(), as sys.exit() is equivalent to 'raise SystemExit()' and can be caught and have other unintended side effects. This will ensure a quick exit with no corrupt files left around, no compile processes still running, no detached GUI, and no file descriptors remaining locked on Windows. -Fixed qtcreator projects not honoring c++11 support -Moved library verification out of build and into _make(), and made failed library verification a non-clean exit (i.e., ensures GUI is terminated) -Cleaned up some overly verbose logging that wasn't running previously due to the way SIGINT was being handled, but is now getting run again. #review-15572 |
||
#2 | 11998 | ShadauxCat |
-Fixed qtcreator project generator not working. This has been fixed locally for a while and I forgot to check it in. -Fixed error with toolchain_darwin on non-mac systems |
||
#1 | 11602 | ShadauxCat | Moving to the proper development directory structure | ||
//guest/ShadauxCat/csbuild/Mainline/project_generator_qtcreator.py | |||||
#1 | 11496 | ShadauxCat | Initial checkin: Current states for csbuild and libSprawl |