#!python3
# Copyright 2007-2017 Gemr. All Rights Reserved.
# Licensed to MIT see LICENSE.txt
import os
import shutil
import warnings
import gdeps as GDeps
# MONKEYPatching : http://www.kaarsemaker.net/blog/2013/10/10/cdata-support-in-elementtree/
# https://github.com/seveas/python-hpilo.git
import xml.etree.ElementTree as etree
__author__ = 'Suryavarman (http://sourceforge.net/u/suryavarman/profile/)'
if hasattr(etree, '_serialize_xml'):
etree._original_serialize_xml = etree._serialize_xml
def _serialize_xml(write, elem, *args, **kwargs):
if elem.tag == 'str':
write("<%s>\n <![CDATA[%s]]>\n </%s>\n " % (elem.tag, elem.text.strip(), elem.tag))
return
if elem.tag == 's':
write("<%s>\n <![CDATA[%s]]>\n </%s>\n " % (elem.tag, elem.text.strip(), elem.tag))
return
return etree._original_serialize_xml(write, elem, *args, **kwargs)
etree._serialize_xml = etree._serialize['xml'] = _serialize_xml
else:
raise RuntimeError("Don't know how to monkeypatch XML serializer workarounds. Please report a bug at https://github.com/seveas/python-hpilo")
# https://wiki.python.org/moin/EscapingXml
[docs]class CodeBlocks(GDeps.Ide):
""" Code::Blocks is a free C, C++ and Fortran IDE built
to meet the most demanding needs of its users.
It is designed to be very extensible and fully configurable.
Config KEY username : default value ""
Trouble shooting :
If you have :
cl Command line warning D9024 : unrecognized source file type
the error come from CB :
Sometime CB generate a good command line with a good include
directory :
[...] /IX:\GDeps\trunk\GDeps\gdeps\test\test_codeblocks-solution\dir0 [...]
sometime no :
cl.exe /nologo X:\GDeps\trunk\GDeps\gdeps\test\test_codeblocks-solution\dir0
The reason you project options, you have to define your include in
the directory section and not in the option section.
Good :
Test_Project-0error0warning.cbp
<Compiler>
<Add directory="$(#test_CB_dir)" />
</Compiler>
Bad:
Test_Project-1error.cbp
<Compiler>
<Add option="$(#test_CB_dir)" />
</Compile
"""
ms_KeyAppdataCBDir = "appdatacbdir"
r""" The key config file is "appdatacbdir"
Example :
appdatacbdir = C:\\Users\\Gandi\\AppData\\Roaming\\codeblocks
@remarks your are not oblige to define this key but if you use a daemon
the default user will be the system and not the current session
user. The result of this is Codeblocks cannot find the
default.conf. In this case you have to define the default.conf
directory.
@remarks For each build we use a copy of this one in a temporary
directory
"""
ms_ConfFileName = '\\\\default.conf'
""" Default CodeBlocks config filename. """
def __init__(self, inConfigFile, inSectionName, inCompilers, inCompilersAlias=[]):
GDeps.Ide.__init__(self, inConfigFile, inSectionName, CodeBlocks.getTypeName(), inCompilers, inCompilersAlias)
self.m_ErrorRegexp = r"(.*: error:.*|.*failed.*)"
self.m_WarningRegexp = r".*: warning:.*"
self.m_AddIncludes = []
[docs] def build(self, inProjectPath, inProjectName, inCompiler, inTargetName="all", inLogFilePath="Output_Buid_CodeBlocks.log", inWildCard="cbp", inRebuildAll=True, inIdeParams={}, inCompilerArgs={}):
""" Build the project or the solution file from the current dir.
Keywords arguments :
:param inProjectPath: the project path
:param inProjectName: the project or the solution name
:param inCompiler: allow the ide to know witch compiler it has to
call, like to define the address model
:param inTargetName: with codeblocks always "all"
:param inLogFilePath: define the output file log
:param inWildCard: project wildcard == cbp
solution wildcard = workspace
:param inRebuildAll: by default we rebuild all. This is a
chronophage but cleaner. default( True )
:param inIdeParams: To define GCV (Global compiler variable) use
this syntax :
inPrams[#MY_GCV] = "C:\PATH\MY\GCV"
:param inCompilerArgs: Send the associate compilers arguments.
:type inCompilerArgs: dict { str : array of str }
>>> {['CXXFLAGS'] : ['-g -std=c++11 -Wall -pedantic']}
or it can be like that
>>> {['CXXFLAGS'] : ['-g', '-std=c++11', '-Wall', '-pedantic']}
Actually the the key doesn't matter. All it's a compiler option.
:todo: USe the compiler option flags 'CFLAGS' and 'CXXFLAGS'
"""
self.beginGCV(inIdeParams)
self.parse_ide_option_args(inIdeParams)
self.parse_ide_option_args(inCompilerArgs)
try:
""" @link http://wiki.codeblocks.org/index.php?title=Code::Blocks_command_line_arguments """
# the compiler generate the log file
self.m_ErrorRegexp = inCompiler.m_ErrorRegexp
self.m_WarningRegexp = inCompiler.m_WarningRegexp
self.m_SplitRegexp = inCompiler.m_SplitRegexp
aBuildType = " --rebuild " if inRebuildAll else " --build "
aAppDataCBDir = " --user-data-dir=" + '"' + self.getTemporaryCBConfigDir() + '"'
# aProjectFullFileName = ".\\" + inProjectName + "." + inWildCard
aProjectFullFileName = os.path.normpath(inProjectPath + "\\" + inProjectName + "." + inWildCard)
assert os.path.exists(aProjectFullFileName), " The project file doesn't exist : " + aProjectFullFileName
# see --script=<str> specify a script file to run after loading
# www.squirrel-lang.org
# http://wiki.codeblocks.org/index.php/Scripting_Code::Blocks
# http://wiki.codeblocks.org/index.php/Variable_expansion
# http://wiki.codeblocks.org/index.php/Scripting_commands#ProjectFile
# http: // wiki.codeblocks.org / index.php / Startup_script # Example
# Should we check the compiler ?
# local CompilerId = base.GetCompilerID();
# if (CompilerId.Matches(_T("gcc")))
# SetActiveBuildTarget
# http://wiki.codeblocks.org/index.php/Build_scripts
# http://forums.codeblocks.org/index.php?topic=16475.0
# aProject.GetBuildTarget().AddCompilerOption()
# aProject.GetBuildTarget().AddIncludeDir('C:\Program Files (x86)\Microsoft DirectX SDK (November 2008)\Include')
# aProject.GetBuildTarget().AddResourceIncludeDir()
# aProject.GetBuildTarget().AddLibDir()
# aProject.GetBuildTarget().AddCommandsBeforeBuild()
# aProject.GetBuildTarget().AddCommandsAfterBuild()
aStartupScriptStr = ('Log(_T("Running startup script"));\n'
' local aProject = GetProjectManager().GetActiveProject();\n'
' if (!IsNull(aProject))\n'
' {\n'
' aProject.SetActiveBuildTarget(_T("' + inTargetName + '"));\n'
' }\n'
' function SetBuildOptions(base)\n'
' {'
' if (PLATFORM == PLATFORM_MSW)\n'
' {\n')
for include in self.m_AddIncludes:
aStartupScriptStr += ' aProject.GetBuildTarget().AddIncludeDir("' + include + '")'
if inCompilerArgs: # to avoid AttributeError: 'NoneType' object has no attribute 'items'
for aKey, aValue in inCompilerArgs.items():
for aOption in aValue:
aStartupScriptStr += 'base.AddCompilerOption(_T("' + aOption + '"));\n' # example -Wno-attributes
aStartupScriptStr += (' }\n'
' } // end of SetBuildOptions\n')
aStartupScriptFileName = inProjectPath + "\\" + inCompiler.m_TypeName + '_' + inTargetName + '.script'
with open(aStartupScriptFileName, 'w') as aScripFile:
aScripFile.write(aStartupScriptStr)
aScriptCmd = ' --script="' + aStartupScriptFileName + '"'
GDeps.call('"' + self.m_Dir + "\\codeblocks.exe" + '"' + aBuildType + '"' + aProjectFullFileName + '"' + " --target=" + '"' + inTargetName + '"' + aAppDataCBDir + aScriptCmd + " --debug-log --no-check-associations --no-splash-screen > " + '"' + inLogFilePath + '"', False)
except IOError:
self.endGCV()
print('cannot build', inProjectName)
else:
self.endGCV()
[docs] def read(self, inSection):
GDeps.Ide.read(self, inSection)
self.m_CBConfigDir = os.environ['APPDATA'] + '\\codeblocks'
if not os.path.isdir(self.m_CBConfigDir):
self.m_CBConfigDir = os.environ['APPDATA'] + '\\CodeBlocks'
r""" The Codeblocks configuration file directory where's is the
default.conf.
By default is is the current App data dir
Example :
C:\\Users\\UserName\\AppData\\Roaming\\codeblocks
or
C:\\Users\\UserName\\AppData\\Roaming\\CodeBlocks
The key config file is GDeps.CodeBlocks.ms_KeyAppdataCBDir
"""
if CodeBlocks.ms_KeyAppdataCBDir in inSection:
self.m_CBConfigDir = inSection[CodeBlocks.ms_KeyAppdataCBDir]
aDefaultConfPath = os.path.normpath(self.m_CBConfigDir + GDeps.CodeBlocks.ms_ConfFileName)
if not os.path.isfile(aDefaultConfPath):
warnings.warn("The appdata environment variable doesn't match with an existing folder. We have to use the "
"gdeps «codeblocks_default.conf»", ResourceWarning)
self.m_CBConfigDir = os.path.normpath(os.path.dirname(os.path.abspath(__file__)))
aDefaultConfFilePath = os.path.normpath(self.m_CBConfigDir + r'\\codeblocks_default.conf')
aDefaultConfPath = os.path.normpath(self.m_CBConfigDir + GDeps.CodeBlocks.ms_ConfFileName)
if os.path.isfile(aDefaultConfFilePath):
""" We create a copy of codeblocks_default.conf with the name default.conf into the gdeps library
directory.
:todo: Use a temporary directory
"""
shutil.copy(aDefaultConfFilePath, aDefaultConfPath)
else:
assert os.path.exists(aDefaultConfPath, 'We cannot find the the CodeBlocks sample default.conf : ' + aDefaultConfPath)
[docs] def write(self):
aSection = GDeps.Ide.write(self)
aSection[CodeBlocks.ms_KeyAppdataCBDir] = self.m_CBConfigDir
return aSection
@staticmethod
[docs] def getTypeName():
return "CodeBlocks"
@staticmethod
[docs] def getTemporaryCBConfigDir():
""" return a directory ( the current dir for the build ) to:
- copy the self.m_CBConfigDir
- modify this one
- build with this Config file
"""
return os.getcwd()
[docs] def beginGCV(self, inParams):
""" @link https://www.microsoft.com/security/portal/mmpc/shared/variables.aspx
"""
self.m_XmlTmpPath = self.getTemporaryCBConfigDir() + GDeps.CodeBlocks.ms_ConfFileName
if os.path.exists(self.m_CBConfigDir):
self.m_XmlPath = self.m_CBConfigDir + GDeps.CodeBlocks.ms_ConfFileName
shutil.copy2(self.m_XmlPath, self.m_XmlTmpPath)
else:
assert False, 'We cannot find the AppData\\Roaming\\codeblocks folder.'
pass
if os.path.exists(self.m_XmlTmpPath):
# http://lxml.de/2.1/api.html
# set the global variables
# aXMLParser = etree.XMLParser(strip_cdata=False)
aTree = etree.parse(self.m_XmlTmpPath)
aRoot = aTree.getroot()
# for child in aRoot.iterfind('.//str'):
# print('tag : ' + child.tag)
# print('attrib : ' + str(child.attrib))
# print('tail : ' + str(child.tail))
# print('text : ' + child.text)
# print('child : ' + str(child))
# https://docs.python.org/3/library/xml.etree.elementtree.html
# access to : CodeBlocksConfig / gcv / sets / default / wx / BASE / str = <![CDATA[D:\Working\GDeps\trunk\GDeps\gdeps\test\codeblocks\wxMSW-2.8.12]]>
# http://www.kaarsemaker.net/blog/2013/10/10/cdata-support-in-elementtree/
# http://www.crummy.com/software/BeautifulSoup/bs4/doc/
print(inParams)
for aKey, aValue in inParams.items():
if "#" in aKey:
# print(aKey)
aGCV = aKey[1:]
aGCV = aGCV.lower()
# print(aGCV)
# print( aRoot.findall( './/gcv/sets/default/' + aGCV ) )
# aCDATAValue = '<![CDATA[' + aValue + ']]>'
aTreeKeyGCV = './/gcv/sets/default/' + aGCV
aKeyGCVs = aRoot.findall(aTreeKeyGCV)
if not aKeyGCVs: # create the GCV child
# print('not find')
for aDefault in aRoot.iterfind('.//gcv/sets/default'):
aKeyNode = etree.SubElement(aDefault, aGCV)
aBase = etree.SubElement(aKeyNode, 'BASE')
aStr = etree.SubElement(aBase, 'str')
aStr.text = aValue
# print('Add ' + aStr.text)
else:
for aIt in aRoot.findall(aTreeKeyGCV + '/BASE/str'):
# print(aIt)
# print(aIt.text)
aIt.text = aValue
aTestFindGCV = False
for aIt in aRoot.findall(aTreeKeyGCV + '/BASE/str'):
if not aTestFindGCV:
aTestFindGCV = aIt.text == aValue
assert aTestFindGCV, 'The ' + aGCV + ' adding/setting in the conf file has failed.'
# https://github.com/behave/behave/issues/201
aTree.write(self.m_XmlTmpPath)
else:
assert False, "The file " + self.m_XmlTmpPath + " doesn't exist!"
[docs] def endGCV(self):
shutil.copy2(self.m_XmlTmpPath, self.m_XmlTmpPath + '.log')
os.remove(self.m_XmlTmpPath)
[docs] def parse_ide_option_args(self, parameters):
for key, param in parameters.items():
if GDeps.Keys.ms_IdeAddInclude == key:
self.m_AddIncludes.append(param)