diff --git a/helpers/helperslib/BuildSystem.py b/helpers/helperslib/BuildSystem.py index ccf3504..0e7d082 100644 --- a/helpers/helperslib/BuildSystem.py +++ b/helpers/helperslib/BuildSystem.py @@ -1,195 +1,195 @@ import os import sys import subprocess import multiprocessing from lxml import etree from helperslib import CommonUtils # Detect whether this build system is able to build the project whose source code is at the given location def detect( pathToSources ): # These are our supported build systems supportedSystems = [ CMake, AutoTools ] # Go over each one in turn for bSystem in supportedSystems: # Does this one support building it? if bSystem.canConfigure(pathToSources): # Return it for use return bSystem print("Unknown build system in", pathToSources) # Otherwise we don't support it return None def substituteCommandTokens( command, sources = '', installPrefix = '' ): # Determine the number of CPUs we should use # Because some systems have a limited number of cores, we do some sanity tests here to ensure we don't end up compiling with -j1 cpuCount = int(multiprocessing.cpu_count()) if cpuCount >= 4: cpuCount = int(cpuCount * 0.75) # Perform the substitution return command.format( sources = sources, installationPrefix = installPrefix, maximumLoad = int(multiprocessing.cpu_count()), cpuCount = cpuCount ) # Base Class for build systems, providing logic to detect the system in use and configure a build using it class CMake(object): # Detect whether this build system is able to build the project whose source code is at the given location @staticmethod def canConfigure( pathToSources ): # Do we have a CMakeLists.txt file? # If we do, it uses a CMake build system if os.path.isfile( os.path.join(pathToSources, 'CMakeLists.txt') ): return True # Otherwise this isn't a CMake build system return False # Configure the build system in the specified directory, using sources at the given location, to be installed at the given location # Ensure that any appropriate directives in the build specification are honoured @staticmethod def configure( buildDirectory, pathToSources, installPrefix, buildSpecification, buildEnvironment ): # Begin building up our configure command cmakeCommand = ['cmake'] # We want a Debug build to allow for good backtraces cmakeCommand.append('-DCMAKE_BUILD_TYPE=Debug') - # Enable ASAN for our builds - cmakeCommand.append("-DECM_ENABLE_SANITIZERS='address'") # We want tests to be built! cmakeCommand.append('-DBUILD_TESTING=ON') # And we want to be installed in a given directory cmakeCommand.append('-DCMAKE_INSTALL_PREFIX="' + installPrefix + '"') # Are we on LInux? if sys.platform == 'linux' and not 'ANDROID_NDK' in buildEnvironment: # Then we also want Coverage by default cmakeCommand.append('-DBUILD_COVERAGE=ON') + # We also want to enable ASAN for our builds + cmakeCommand.append("-DECM_ENABLE_SANITIZERS='address'") # Are we on Windows? if sys.platform == 'win32': # We want a NMake based build, rather than the default MSBuild cmakeCommand.append('-G "NMake Makefiles JOM"') # Are we building for Android? if 'ANDROID_NDK' in buildEnvironment and sys.platform == 'linux': # We want CMake to cross compile appropriately cmakeCommand.append('-DCMAKE_TOOLCHAIN_FILE="/opt/kdeandroid-deps/share/ECM/toolchain/Android.cmake"') cmakeCommand.append('-DKF5_HOST_TOOLING=/opt/nativetooling/lib/x86_64-linux-gnu/cmake/') # CMake also needs additional guidance to find things # First though, apply a necessary transformation to allow CMake to parse the list we pass it ecmAdditionalRoots = buildEnvironment['CMAKE_PREFIX_PATH'].replace(':', ';') # Then give that list to CMake cmakeCommand.append('-DECM_ADDITIONAL_FIND_ROOT_PATH="' + ecmAdditionalRoots + '"') # We also don't want ASAN as that is only half available in the Android NDK cmakeCommand.remove("-DECM_ENABLE_SANITIZERS='address'") # Finally we drag in options specified by the build specification cmakeCommand.append( buildSpecification['cmake-options'] ) # Lucky last, we add the path to our sources cmakeCommand.append( '"' + pathToSources + '"' ) # Now glue it all together and substitute any tokens we need to swap out commandToRun = ' '.join( cmakeCommand ) commandToRun = substituteCommandTokens( commandToRun, sources = pathToSources, installPrefix = installPrefix ) print( commandToRun ) # Run the command try: subprocess.check_call( commandToRun, stdout=sys.stdout, stderr=sys.stderr, shell=True, cwd=buildDirectory, env=buildEnvironment ) except Exception: return False # Did we succeed? return True @staticmethod def convertCTestResultsToJUnit( buildDirectory ): # Where is the base prefix for all test data for this project located? testDataDirectory = os.path.join( buildDirectory, 'Testing' ) # Determine where we will find the test run data for the latest run filename = os.path.join( testDataDirectory, 'TAG' ) with open(filename, 'r') as tagFile: testDirectoryName = tagFile.readline().strip() # Open the test result XML and load it filename = os.path.join( testDataDirectory, testDirectoryName, 'Test.xml' ) with open(filename , 'r', encoding='UTF-8') as xmlFile: xmlDocument = etree.parse( xmlFile ) # Load the XSLT file filename = os.path.join( CommonUtils.scriptsBaseDirectory(), 'templates', 'ctesttojunit.xsl' ) with open(filename, 'r') as xslFile: xslContent = xslFile.read() xsltRoot = etree.XML(xslContent) # Transform the CTest XML into JUnit XML transform = etree.XSLT(xsltRoot) return transform(xmlDocument) # Base Class for build systems, providing logic to detect the system in use and configure a build using it class AutoTools(object): # Detect whether this build system is able to build the project whose source code is at the given location @staticmethod def canConfigure( pathToSources ): # Do we have either a ./configure or ./autogen.sh script? # If we do, then we probably have a Autotools build system firstConfigureCommand = CommonUtils.firstPresentFile( pathToSources, ['configure', 'autogen.sh'] ) if firstConfigureCommand != '': return True # Otherwise this isn't an Autotools based project return False # Configure the build system in the specified directory, using sources at the given location, to be installed at the given location # Ensure that any appropriate directives in the build specification are honoured @staticmethod def configure( buildDirectory, pathToSources, installPrefix, buildSpecification, buildEnvironment ): # Do we need to do any bootstrapping first? Let's see what files we have on hand... configureCommand = CommonUtils.firstPresentFile( pathToSources, ['configure', 'autogen.sh'] ) configureTemplate = CommonUtils.firstPresentFile( pathToSources, ['configure.in', 'configure.ac'] ) # Begin the process of trying to configure Autotools to build this project try: # Do we have autogen.sh and a configure template? if configureCommand == 'autogen.sh' and configureTemplate != '': # Then we need to run autogen.sh to get the ball rolling commandToRun = os.path.join( pathToSources, configureCommand ) subprocess.check_call( commandToRun, stdout=sys.stdout, stderr=sys.stderr, shell=True, cwd=pathToSources, env=buildEnvironment ) # With autogen.sh done, did it leave behind a Makefile we need to cleanup? if os.path.exists( os.path.join(pathToSources, 'Makefile') ): # Then run make distclean and hope that cleans it up subprocess.check_call( "make distclean", stdout=sys.stdout, stderr=sys.stderr, shell=True, cwd=pathToSources, env=buildEnvironment ) # Now re-determine the configure command we should be using configureCommand = CommonUtils.firstPresentFile( pathToSources, ['configure', 'autogen.sh'] ) # Now we can begin building up the configure command autotoolsCommand = [ os.path.join(pathToSources, configureCommand) ] # Install into our install prefix autotoolsCommand.append( '--prefix=' + installPrefix ) # Add any custom options from our build specification autotoolsCommand.append( buildSpecification['autotools-options'] ) # Finally, glue the command together and substitute any tokens we need to commandToRun = ' '.join( autotoolsCommand ) commandToRun = substituteCommandTokens( commandToRun, sources = sources, installPrefix = installPrefix ) # Now we can run it! subprocess.check_call( commandToRun, stdout=sys.stdout, stderr=sys.stderr, shell=True, cwd=pathToSources, env=buildEnvironment ) except Exception: return False # We succeeded return True